Contents

Build Sites with a Container

On this page

Use the builder container when Python should not be installed directly on the host. The private Markdown is mounted read-only, and generated files are written to a separate host directory.

Build the builder image#

Run this command from the Simple Docs repository:

podman build \
  --target builder \
  -t simple-docs-builder \
  -f Containerfile .

The builder image contains Python, build.py, VERSION, shared assets, and the bundled themes. It does not contain bundled or private documentation source.

Select a theme#

Pass the theme name to the builder command:

--theme paper

To bake the paper theme into the bundled nginx example, use:

podman build \
  --build-arg THEME=paper \
  -t simple-docs-paper \
  -f Containerfile .

Docker accepts the same build argument. See create and select themes for available themes and the authoring contract.

Generate a site with Podman#

Create an output parent directory:

mkdir -p /absolute/path/to/private-project-build

Run the builder with two bind mounts:

podman run --rm --userns=keep-id \
  -v /absolute/path/to/private-project/docs:/input:ro,Z \
  -v /absolute/path/to/private-project-build:/output:Z \
  simple-docs-builder \
  --source /input \
  --output /output/site \
  --archive /output/manual-site.tar.gz

The /input mount is read-only. The builder can write only to /output and temporary locations inside the container.

For multilingual content, mount the source parent containing languages.json and all declared language directories at /input. Keep --source /input. Flat sources also work and produce English output at /en/.

The :Z suffix applies a private SELinux label for Podman. Omit :Z on systems that do not use SELinux labeling.

Generate an offline ZIP#

The same builder can create a ZIP that opens without a server. Add this option to the Podman or Docker command:

--offline-archive /output/manual-site-offline.zip

The ZIP is written through the existing /output bind mount. Extract it and open manual-site-offline/index.html.

Optional Mermaid Rendering#

For Mermaid diagrams, use the separate builder-mermaid target. The Make helper builds it as simple-docs-builder-mermaid:

make builder-image MERMAID=1

This optional image adds Node.js, Mermaid CLI, Puppeteer, and Chromium. The standard builder remains Python-only. The npm toolchain uses the committed tools/mermaid/package-lock.json, pinning Mermaid CLI 11.17.0 and Puppeteer 25.11.0. Chromium is installed from Alpine packages and follows the base image, as do the other OS packages; the entire OS/browser build is not pinned.

The container skips Puppeteer's browser download and uses /usr/bin/chromium. Its mmdc wrapper passes -p /opt/mermaid/puppeteer.json, including the container-only no-sandbox setting for root sample builds. Use that wrapper and configuration only inside the container.

After building the image, generate both hosted output and an offline ZIP with networking disabled. Create the output parent as above, then run:

podman run --rm --network=none --userns=keep-id \
  -v /absolute/path/to/private-project/docs:/input:ro,Z \
  -v /absolute/path/to/private-project-build:/output:Z \
  simple-docs-builder-mermaid \
  --source /input \
  --output /output/site \
  --archive /output/manual-site.tar.gz \
  --offline-archive /output/manual-site-offline.zip \
  --mermaid

For multilingual documentation, /input must contain the entire source parent: languages.json and every declared language directory. Generated files remain in the separate output mount. The image build needs package repository access; rendering with the installed toolchain needs no network.

For hosted-only output or a runtime image containing the bundled manual, use:

make bundle-container MERMAID=1
make runtime-image MERMAID=1

The existing theme selection also supplies diagram colors automatically. Use make bundle-container THEME=paper MERMAID=1, or add --theme paper to the direct container command. See Mermaid diagram colors for the custom-theme palette contract and neutral fallback.

Only the build toolchain needs Node.js and a browser. Generated diagrams are self-contained static SVG images in both hosted and offline output; the runtime image and readers do not need Mermaid. See Mermaid authoring for the fixed subset and accessible descriptions.

Generate a site with Docker#

Build the same target:

docker build \
  --target builder \
  -t simple-docs-builder \
  -f Containerfile .

Run it with the host user identity so generated files are not owned by root:

docker run --rm --user "$(id -u):$(id -g)" \
  -v /absolute/path/to/private-project/docs:/input:ro \
  -v /absolute/path/to/private-project-build:/output \
  simple-docs-builder \
  --source /input \
  --output /output/site \
  --archive /output/manual-site.tar.gz

Build for a URL prefix#

Pass the base path after the output options:

podman run --rm --userns=keep-id \
  -v /absolute/path/to/private-project/docs:/input:ro,Z \
  -v /absolute/path/to/private-project-build:/output:Z \
  simple-docs-builder \
  --source /input \
  --output /output/site \
  --archive /output/manual-site.tar.gz \
  --base-path /project-name/

Verify source isolation#

The generated site/ directory and archives contain generated files, not Markdown. The private source is not copied into the builder image or nginx runtime image.

Return to the generating sites overview, compare the Python workflow, use the Make helpers, or continue with serving generated sites. You can also create or select a theme.