Quick start Features Configuration Install Licensing GitHub Get started

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.