Docker runs CrossGlyph without installing Python, uv or the project dependencies
on the host. The container can read and write one mounted workspace. That
folder holds the source fonts, configuration files, downloaded fallback faces
and built .cpfont families.
The preview has no login. Anyone who can reach the address can retune a
family, save a config and start a build, so the supplied Compose service
publishes it on 127.0.0.1 and only the Docker host can open it.
Run every command from the folder that contains compose.yaml. That can be a
Git checkout or an unpacked release ZIP.
| Folder | docker compose uses |
What --local builds from |
|---|---|---|
| Git checkout | ghcr.io/crazycoder/crossglyph:latest |
Current checkout |
| Unpacked release | Image tagged with the release version | versions/<release> |
On Windows:
crossglyph-docker.cmd
crossglyph-docker.cmd --localOn macOS or Linux:
./crossglyph-docker.sh
./crossglyph-docker.sh --localWith no option the launcher uses the published image. --local builds from the
checkout, or from the matching version inside an unpacked release. After the
service is healthy, the launcher prints the actual browser address, mounted
workspace and commands for logs, shutdown and cleanup.
The Docker launchers are optional. If Docker with Compose is unavailable, they
explain how to install it and point to crossglyph.cmd or crossglyph.sh for
running CrossGlyph directly instead.
Compose is the part of Docker that reads a compose.yaml file and runs what
it describes. CrossGlyph ships those files, so the commands below work as
printed from the folder that holds them.
To use the published image:
docker compose up -d --wait
docker compose run --rm crossglyph buildTo build from the code in the current folder instead:
docker compose -f compose.yaml -f compose.build.yaml \
up -d --build --wait
docker compose -f compose.yaml -f compose.build.yaml \
run --rm --build crossglyph buildThe local override names the image crossglyph:local. In a checkout it builds
the checkout. In an unpacked release it builds the matching source under
versions/, so it does not need the published CrossGlyph image.
Docker is the only host tool this path needs. The build installs its own system
packages and uses tools/uv.cmd to download and verify the pinned uv release.
It still needs network access to the Python base image, Debian packages, the uv
release and the Python packages unless those layers and downloads are cached.
When Compose is run directly, open http://127.0.0.1:8000/ after the preview is
healthy. Put TTF or OTF files in the local fonts folder. The running preview
finds workspace changes when the page regains focus.
Follow the preview log:
docker compose logs -fStop a preview that uses the published image:
docker compose downStop a preview that uses the local build:
docker compose -f compose.yaml -f compose.build.yaml downEither command removes the CrossGlyph container and its Compose network. The image and the mounted workspace remain, so the next start is faster and the fonts are safe.
To return the CrossGlyph-specific Docker resources to their state before the
first start, add --rmi all. For the published image:
docker compose down --rmi allFor the local build:
docker compose -f compose.yaml -f compose.build.yaml down --rmi allThese commands stop and remove the container, Compose network and image used by
the service. They do not delete anything from the mounted fonts folder.
One-off commands shown with run --rm remove their containers when they finish.
BuildKit may retain shared build cache and base-image layers. Docker does not provide a safe project-scoped command for removing them. Global prune commands can remove cache and resources used by unrelated projects, so they are not part of this cleanup.
Everything in the image runs crossglyph, so a word after the Compose service
name is a CrossGlyph command. It replaces the default preview command and keeps
the mounted workspace and the container restrictions.
docker compose run --rm crossglyph build
docker compose run --rm crossglyph build --force
docker compose run --rm crossglyph build notosans
docker compose run --rm crossglyph fetch-fallbacks
docker compose run --rm crossglyph --versionDocker owns the preview process lifetime. The native start, stop, status
and restart commands are unavailable in a container, so they cannot create
daemon state or detach a child process there. Use docker compose up,
docker compose down and docker compose ps instead.
Builds read source fonts from the workspace root and configs from conf/.
They write to cpfonts/ unless out in conf/all.conf selects another path.
Use a path relative to the workspace so that the output remains in the mounted
folder.
The workspace has the same layout as a native CrossGlyph installation:
fonts/
MyFamily-Regular.ttf
MyFamily-Bold.ttf
conf/
all.conf
myfamily.conf
fallbacks/
cpfonts/
Compose reads the following environment variables. You can set them in the
shell or in a .env file beside compose.yaml.
| Variable | Default | Purpose |
|---|---|---|
CROSSGLYPH_WORKSPACE |
./fonts |
The only host folder mounted into the container |
CROSSGLYPH_PORT |
8000 |
The host port for the preview |
CROSSGLYPH_BIND |
127.0.0.1 |
The host address that publishes the preview |
CROSSGLYPH_UID |
1000 |
The user ID that writes workspace files on Linux |
CROSSGLYPH_GID |
1000 |
The group ID that writes workspace files on Linux |
CROSSGLYPH_TAG |
Release version in an installed ZIP; latest in a checkout |
The image tag to run |
On Linux, set CROSSGLYPH_UID and CROSSGLYPH_GID to the owner of the
workspace if that account does not use IDs 1000 and 1000. Docker Desktop
manages bind mount permissions on Windows and macOS.
Use an absolute host path with docker run. The image sets the preview host to
0.0.0.0, so both the default command and an explicit preview command accept
connections through the published port.
docker run --rm \
--read-only --tmpfs /tmp \
--cap-drop ALL --security-opt no-new-privileges \
--mount type=bind,source=/absolute/path/to/fonts,target=/workspace \
-p 127.0.0.1:8000:8000 \
ghcr.io/crazycoder/crossglyph:latestReplace the port option with a CLI command for a one-off build:
docker run --rm \
--read-only --tmpfs /tmp \
--cap-drop ALL --security-opt no-new-privileges \
--mount type=bind,source=/absolute/path/to/fonts,target=/workspace \
ghcr.io/crazycoder/crossglyph:latest buildThe preview checks latest.json, the small file on the web that names the
newest release, at startup and when you press Check now. If the selected
image is behind, the version row names the release and says to pull the new
image. The check state stays in the container's private temporary filesystem
rather than adding a file to the mounted workspace.
An installed ZIP defaults to its own version so native and container launches
run the same code. Set CROSSGLYPH_TAG in .env to move a container-only
deployment to another version, or to latest to follow each release. Then pull
the selected image and recreate the service:
docker compose pull
docker compose up -d --waitThe workspace is outside the container, so this does not replace fonts, configs, fallbacks or output.
A native crossglyph update replaces untouched root compose.yaml and
compose.build.yaml files with the ones for the new release. If you edited
either file, the update keeps it and writes <name>.new beside it. Put
deployment settings in .env so the managed Compose files can update without
a conflict.
Published images support linux/amd64 and linux/arm64. Each release also
carries build provenance in the GitHub Container Registry, along with an SBOM,
which is a list of everything that went into the image.
The Compose service runs as a non-root user. Its application filesystem is
read-only, Linux capabilities are removed, and privilege escalation is
disabled. /tmp is temporary. /workspace is the only host path the service
can write.
CrossGlyph can change every file in the mounted workspace. Do not mount a parent directory or a folder that contains unrelated files. The container does not mount the Docker socket.
Do not publish the preview directly on 0.0.0.0. The current server has no
login, and its save and build endpoints write to the workspace. Put an
authenticated TLS reverse proxy in front of CrossGlyph before making it
available on another machine.
The mounted folder is the only way fonts get in and builds get out.