Docker
Faber runs as a Docker container. The container must be privileged and see the host’s cgroup tree, because Faber creates namespaces and cgroups for every task. That makes the container root-equivalent on its host: give Faber a machine or VM of its own. See the isolation tier.
Requirements
Section titled “Requirements”- Linux 5.8 or newer, with cgroup v2 (
stat -fc %T /sys/fs/cgroupprintscgroup2fs). - Rootful Docker. Rootless Docker cannot grant the nested mount operations.
- x86_64 or ARM64.
Images
Section titled “Images”| Image | Contents |
|---|---|
vgfractal/faber:latest | Faber plus GCC, G++ and Make. Moves with every push to main |
vgfractal/faber:base | Faber and its runtime dependencies only, no compilers. The base for your own images |
vgfractal/faber:<version> | A released version, for example 0.3.0. Pin one in production |
Both architectures are published under every tag.
docker run -d --name faber --init \ --privileged --cgroupns=host \ -v /sys/fs/cgroup:/sys/fs/cgroup:rw \ --read-only --tmpfs /tmp:size=64m \ --memory 8g --memory-swap 8g --pids-limit 4096 \ --restart unless-stopped \ -e API_KEY="$API_KEY" \ -p 127.0.0.1:3000:3000 \ vgfractal/faber:0.3.0| Flag | Why |
|---|---|
--privileged, --cgroupns=host, -v /sys/fs/cgroup:/sys/fs/cgroup:rw | Required. Faber creates namespaces and cgroups beneath its own container’s cgroup, and refuses to start if the cgroup namespace and the mount disagree |
--init | Reaps supervisor processes orphaned by an abnormal exit; Faber is not an init process |
--read-only, --tmpfs /tmp:size=64m | Faber writes only small sandbox root skeletons, under /tmp |
--memory, --memory-swap, --pids-limit | A backstop above Faber’s own aggregate limits. Size them with the sizing formula |
-p 127.0.0.1:3000:3000 | Faber speaks plain HTTP; keep it on localhost and put a TLS proxy in front |
Configure it with environment variables. The
container stops cleanly on SIGTERM within SHUTDOWN_TIMEOUT_MS (5 s), below
Docker’s default 10 s grace period.
Check it:
curl -s http://localhost:3000/api/v1/healthdocker logs faberAdd toolchains
Section titled “Add toolchains”Tasks see the image’s /usr, /bin and /lib read-only, so a language is
available to tasks once it is installed in the image. Start from the base
image and add only what you serve; the Dockerfile
builder writes the Dockerfile for you.
A hand-written example:
FROM vgfractal/faber:base
RUN apt-get update \ && apt-get install -y --no-install-recommends gcc g++ libc6-dev make python3 \ && rm -rf /var/lib/apt/lists/*
ENV FABER_TOOLCHAINS=c@12,cpp@12,python@3.11Things to get right:
- Install under a read-only path. Tasks see only the paths in
SANDBOX_READONLY_PATHS. If a toolchain needs files elsewhere (the JDK reads/etc/java-17-openjdk), add that path, keeping the defaults in the list. - Put commands on the sandbox
PATH. Tasks do not inherit the image’sENV. TheirPATHis/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/binunless the request sets one, so link binaries installed elsewhere (for example/usr/local/go/bin/go) into/usr/local/bin, or call them by absolute path. - Set
FABER_TOOLCHAINSsoGET /infocan tell clients what the image offers, andFABER_IMAGE_IDif you enable the step cache. - Test every language under the default profile before relying on the
image.
scripts/toolchain-test.shruns each language’s example against a running instance (API_URL,API_KEY);scripts/vm.sh test-toolchainsdoes it inside a disposable VM.
Expose it safely
Section titled “Expose it safely”Faber has no TLS. Terminate TLS in a reverse proxy on the same host and
forward only /api/v1/. With Caddy:
faber.example.com { handle /api/v1/* { reverse_proxy 127.0.0.1:3000 } respond 404}With nginx, also raise the body size and the read timeout:
location /api/v1/ { proxy_pass http://127.0.0.1:3000; client_max_body_size 64m; # at least UPLOAD_FILE_LIMIT_BYTES proxy_read_timeout 60s; # above OVERALL_TIMEOUT_MS}Only your own servers should hold the API key. Do not let browsers call Faber directly.
Upgrades
Section titled “Upgrades”Pin a version tag, and upgrade by replacing the container. To avoid failed
requests, take the instance out of the load balancer, wait until
faber_execution_slots_in_use is 0, then replace it. See the
runbook.