Hermit is published to DockerHub as beavuck/hermit. The image is a statically-linked binary running on a scratch
base -- no OS, no shell, minimal attack surface. It supports linux/amd64 and linux/arm64.
To build the docker image locally, do make sure to auth first, as we're using a Docker Hardened Image:
echo "$DOCKERHUB_WRITE_TOKEN" | docker login --username "$DOCKERHUB_USERNAME" --password-stdinTo build and load a single-platform image for local testing:
docker build --load --tag "beavuck/hermit" .To verify the multi-platform build locally, you need a buildx builder that uses the docker-container driver (the
default docker driver does not support multi-platform). Create one once:
docker buildx create --name multiarch --useMulti-platform images cannot be loaded into the local Docker daemon -- they must be pushed directly to a registry or stay in the build cache:
docker buildx build --platform linux/amd64,linux/arm64 --tag "beavuck/hermit" .To also push, you may add --push. But the CI pipeline should take care of that, typically.
Mount your OpenAPI spec files into /specs. Use HERMIT_SPECS_DIR to load all files in a directory:
docker run --rm --pull=always \
-p 8532:8532 \
-v ./specs_assets:/specs:ro \
-e HERMIT_SPECS_DIR=specs \
beavuck/hermitOr use HERMIT_SPECS to load specific files (comma-separated):
docker run --rm --pull=always \
-p 8532:8532 \
-v ./specs_assets:/specs:ro \
-e HERMIT_SPECS=specs/taskflow.openapi.yml,specs/dog_cafe.openapi.json \
beavuck/hermitHERMIT_SPECS_DIR and HERMIT_SPECS are mutually exclusive -- exactly one must be provided.
See options.md.
Here is a docker-compose example of a typical Hermit setup:
hermit:
image: beavuck/hermit:latest
pull_policy: always
environment:
HERMIT_SPECS_DIR: specs
ports:
- "8532:8532"
volumes:
- ./specs_assets:/specs:roscratch base -- the binary is compiled with RUSTFLAGS="-C target-feature=+crt-static" targeting a musl Linux
triple, producing a fully static binary with no libc dependency.
Multi-platform build -- the Dockerfile uses --platform=$BUILDPLATFORM on the builder stage so it always runs
natively on the CI host, regardless of the target architecture. ARG TARGETARCH receives the target from
docker buildx (amd64 or arm64) and is mapped to the corresponding Rust target triple. cargo-zigbuild uses zig
as a cross-compiler, eliminating the need for platform-specific C toolchains.
Fixed binary path before scratch -- the builder copies the binary to /hermit-bin before the final stage.
scratch has no shell, so COPY --from=builder cannot evaluate $(cat /rust_target). A fixed path sidesteps this.
PID 1 signal handling -- Linux gives PID 1 special treatment: it ignores signals unless it explicitly handles them.
Without a signal handler, docker stop would wait 10 seconds then force-kill the container. Hermit registers handlers
for both SIGTERM and SIGINT so it shuts down immediately.
--target flag and proc-macros -- building without an explicit target on the Alpine builder uses
x86_64-alpine-linux-musl as the host target, which applies RUSTFLAGS to all crates including proc-macros.
Proc-macro crates must be compiled as dylibs, which is incompatible with crt-static on musl. Specifying --target
separates host builds (proc-macros, no RUSTFLAGS) from target builds (the binary, with RUSTFLAGS).