Installation
gomddoc ships as one statically linked binary for Linux and macOS on amd64 and arm64, plus a multi-arch container image. Every channel below installs the same binary, and none of them needs a runtime or package dependencies.
Installing is free. Running gomddoc for a business needs a commercial licence.
Install script
The quickest route on Linux and macOS:
curl -fsSL https://raw.githubusercontent.com/monolithiclab/gomddoc/main/scripts/install.sh | sh
The script detects your OS and architecture, downloads the matching release archive and checks it against the release's SHA256SUMS file. It refuses to install when no SHA-256 tool is available, so the checksum step is never silently skipped.
The binary goes to /usr/local/bin, using sudo when that directory is not writable, and to ~/.local/bin when sudo is unavailable. The script warns you if the target directory is not on your PATH.
Signature verification
SHA256SUMS is served from the same place as the archive, so on its own it only proves the download arrived intact. When cosign is on your PATH, the script also verifies the keyless Sigstore signature over SHA256SUMS. The signing identity is pinned to this repository's release workflow at the exact tag you are installing, which is what proves the binary came from the official release pipeline.
A signature that fails to verify always aborts the install. When cosign is not installed, the script prints a warning and continues; set GOMDDOC_REQUIRE_COSIGN=1 to make that an abort too. Install cosign first for a provenance-checked install.
Environment variables
| Variable | Effect | Default |
|---|---|---|
GOMDDOC_VERSION |
Release to install, with or without the leading v |
Latest release |
GOMDDOC_INSTALL_DIR |
Target directory | /usr/local/bin, then ~/.local/bin |
GOMDDOC_REQUIRE_COSIGN |
Abort when cosign is not installed | Unset (warn and continue) |
Pin a version and install into your home directory:
curl -fsSL https://raw.githubusercontent.com/monolithiclab/gomddoc/main/scripts/install.sh \
| GOMDDOC_VERSION=0.1.3 GOMDDOC_INSTALL_DIR="$HOME/.local/bin" sh
Homebrew
A formula is published to the monolithiclab/tap tap for macOS and Linux:
brew install monolithiclab/tap/gomddoc
Upgrade with the usual command:
brew upgrade gomddoc
Go toolchain
go install builds from source and needs Go 1.26 or later:
go install github.com/monolithiclab/gomddoc/cmd/gomddoc@latest
The binary lands in $GOBIN, which defaults to $HOME/go/bin. Make sure that directory is on your PATH.
Release archives
Each GitHub release carries four archives, built with CGO_ENABLED=0:
| OS | Architectures |
|---|---|
| Linux | amd64, arm64 |
macOS (darwin) |
amd64, arm64 |
There is no Windows build. On Windows, use Docker or WSL.
Archives are named gomddoc_<version>_<os>_<arch>.tar.gz and contain the binary, LICENSE, LICENSE-COMMERCIAL.md and README.md. The version is part of the file name, so substitute the release you want:
VERSION=0.1.3
OS=darwin # or linux
ARCH=arm64 # or amd64
curl -fLO "https://github.com/monolithiclab/gomddoc/releases/download/v${VERSION}/gomddoc_${VERSION}_${OS}_${ARCH}.tar.gz"
tar xzf "gomddoc_${VERSION}_${OS}_${ARCH}.tar.gz" gomddoc
sudo mv gomddoc /usr/local/bin/
Verifying by hand
Every release also publishes SHA256SUMS and SHA256SUMS.sigstore.json, a Sigstore bundle holding the keyless cosign signature. Releases up to v0.1.2 carry a detached SHA256SUMS.sig and SHA256SUMS.pem instead. Download both files from the same release, next to the archive, then run:
cosign verify-blob --bundle SHA256SUMS.sigstore.json \
--certificate-identity "https://github.com/monolithiclab/gomddoc/.github/workflows/release.yml@refs/tags/v${VERSION}" \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
SHA256SUMS
sha256sum --ignore-missing -c SHA256SUMS # macOS: shasum -a 256 --ignore-missing -c SHA256SUMS
The install script performs exactly these checks for you.
Docker
The image is published to GitHub Container Registry and signed with cosign:
docker pull ghcr.io/monolithiclab/gomddoc:latest
| Tag | Contents |
|---|---|
latest |
Newest stable release, multi-arch. Prereleases do not move it. |
<version> |
A specific release, e.g. 0.1.3, multi-arch manifest |
<version>-amd64, <version>-arm64 |
Single-architecture images |
The image is built on gcr.io/distroless/static-debian12:nonroot. It holds the gomddoc binary at /gomddoc and nothing else: no shell, no package manager. It runs as the nonroot user (UID 65532), exposes port 8080 and weighs about 30 MB. Theme assets are embedded in the binary.
The entrypoint is the bare binary, so every docker run names a subcommand.
Serve a local directory
docker run --rm -p 8080:8080 -v "$PWD/docs:/content:ro" \
ghcr.io/monolithiclab/gomddoc serve /content
gomddoc only reads content, so mount it read-only. A .gomddoc/config.yml inside the mounted directory is picked up automatically.
Serve a Git repository
docker run --rm -p 8080:8080 ghcr.io/monolithiclab/gomddoc \
serve "git+https://github.com/your-org/your-docs.git#main:docs"
Git sources use the git+https://, git+ssh:// or git:// schemes; a bare https:// URL is treated as a local path. The fragment picks a branch and an optional subdirectory. The repository is shallow-cloned at startup, so restart the container to publish new commits. A .gomddoc/config.yml committed to the repository is not read; set site options through GOMDDOC_* environment variables instead.
Bake your docs into an image
For a self-contained deployable image, copy your content on top of the official one:
FROM ghcr.io/monolithiclab/gomddoc:latest
COPY docs/ /content/
CMD ["serve", "/content"]
Put your .gomddoc/ directory inside docs/ and it travels with the content.
Docker Compose
services:
docs:
image: ghcr.io/monolithiclab/gomddoc:latest
command: ["serve", "/content"]
ports:
- "8080:8080"
volumes:
- ./docs:/content:ro
environment:
GOMDDOC_SITE_META_TITLE: "My Documentation"
read_only: true
restart: unless-stopped
command is required because the image has no default command. read_only: true works as is: serve writes nothing to disk unless you set --git-storage-dir, in which case give that directory a writable volume.
docker compose up -d
Kubernetes
gomddoc exposes /health/live and /health/ready for probes. They stay on the main port even when --admin-port moves metrics elsewhere, and they skip Basic Auth.
apiVersion: apps/v1
kind: Deployment
metadata:
name: docs
spec:
replicas: 2
selector:
matchLabels:
app: docs
template:
metadata:
labels:
app: docs
spec:
securityContext:
runAsNonRoot: true
runAsUser: 65532
containers:
- name: gomddoc
image: ghcr.io/monolithiclab/gomddoc:latest
args: ["serve", "/content", "--admin-port", "0.0.0.0:9090"]
ports:
- name: http
containerPort: 8080
- name: admin
containerPort: 9090
livenessProbe:
httpGet:
path: /health/live
port: http
readinessProbe:
httpGet:
path: /health/ready
port: http
resources:
requests:
cpu: 50m
memory: 32Mi
limits:
cpu: 100m
memory: 64Mi
securityContext:
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
volumeMounts:
- name: content
mountPath: /content
readOnly: true
volumes:
- name: content
configMap:
name: docs-content
---
apiVersion: v1
kind: Service
metadata:
name: docs
spec:
selector:
app: docs
ports:
- name: http
port: 80
targetPort: http
A ConfigMap holds a flat set of files, which suits a small site. For a nested docs tree, bake the content into an image as shown above, or point serve at a Git URL.
Metrics
Prometheus metrics are served at /metrics. With --admin-port set, they move to the admin listener along with pprof. A bare :9090 binds 127.0.0.1, so use 0.0.0.0:9090 when the scraper runs in another pod, and keep the admin port out of the Service: it has no authentication.
metadata:
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "9090"
prometheus.io/path: "/metrics"
Sizing
gomddoc keeps its metadata, search index and navigation tree in memory, plus the whole repository for a Git source without --git-storage-dir. A small site runs in about 35 MB of resident memory. Start with a 64 MB limit and adjust to your content. SIGTERM triggers a graceful shutdown.
Static hosting in CI
gomddoc build renders the whole site to plain files for GitHub Pages, Netlify, S3 or any web server. A static host has no gomddoc process behind it, so the search backend, content negotiation, the MCP endpoint, health checks and metrics are not available. Turn the search button off for static builds:
# .gomddoc/config.yml
theme:
features:
search: false
This GitHub Actions workflow publishes docs/ to GitHub Pages:
name: Docs
on:
push:
branches: [main]
permissions:
contents: read
pages: write
id-token: write
jobs:
deploy:
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- uses: actions/checkout@v4
- name: Install gomddoc
run: |
curl -fsSL https://raw.githubusercontent.com/monolithiclab/gomddoc/main/scripts/install.sh \
| GOMDDOC_INSTALL_DIR="$HOME/.local/bin" sh
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
- name: Build
run: gomddoc build ./docs -o ./public
- uses: actions/upload-pages-artifact@v3
with:
path: ./public
- id: deployment
uses: actions/deploy-pages@v4
Build from source
Building needs Go 1.26 or later, Git and Make:
git clone https://github.com/monolithiclab/gomddoc.git
cd gomddoc
make build
./build/gomddoc serve ./docs
make install puts the binary in $GOBIN instead.
The repository's Dockerfile expects a prebuilt Linux binary in the build context, because the release pipeline cross-compiles it separately. To build the image yourself:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o gomddoc ./cmd/gomddoc
docker build -t gomddoc .
rm gomddoc
Check the install
gomddoc --version
gomddoc preview --open ./docs
preview picks the first free port from 8080, prints the URL and opens it in your browser. Run gomddoc doctor ./docs to check a site's configuration and content, and head to the getting started guide for the next steps.