Skip to content

Docker Compose

The repository ships Compose files under docker/prod/:

FileWhat it runs
docker-compose.yamlOne Faber instance, hardened, bound to localhost
docker-compose.cluster.yamlTwo Faber replicas sharing one Redis for the step cache
Terminal window
export API_KEY="$(openssl rand -hex 32)"
docker compose -f docker/prod/docker-compose.yaml up -d
curl -s http://localhost:3000/api/v1/health

The service definition, abridged:

services:
faber:
image: ${FABER_IMAGE:-vgfractal/faber:latest}
ports:
- '${FABER_BIND_ADDRESS:-127.0.0.1}:${FABER_PORT:-3000}:3000'
environment:
- API_KEY=${API_KEY:?Set API_KEY before starting Faber}
- CACHE_ENABLED=${CACHE_ENABLED:-false}
- MAX_CONCURRENCY=${MAX_CONCURRENCY:-10}
- MEMORY_MAX=${MEMORY_MAX:-256M}
# ... the other limits, each overridable from the shell or an .env file
privileged: true
cgroup: host
init: true
read_only: true
tmpfs:
- /tmp:size=64m
mem_limit: ${FABER_MEMORY_LIMIT:-8g}
memswap_limit: ${FABER_MEMORY_LIMIT:-8g}
pids_limit: ${FABER_PIDS_LIMIT:-4096}
volumes:
- /sys/fs/cgroup:/sys/fs/cgroup:rw
stop_grace_period: 10s
restart: unless-stopped
healthcheck:
test: ['CMD', 'curl', '--fail', '--silent', 'http://localhost:3000/api/v1/health']
security_opt:
- no-new-privileges:true

Override settings through the environment or an .env file next to the Compose file:

VariableDefaultMeaning
FABER_IMAGEvgfractal/faber:latestPin a version (vgfractal/faber:0.3.0) or use your own image
FABER_BIND_ADDRESS, FABER_PORT127.0.0.1, 3000Where the port is published
FABER_MEMORY_LIMIT, FABER_PIDS_LIMIT8g, 4096Container backstop; raise together with MAX_CONCURRENCY (see sizing)
Any Faber setting listed in the fileits defaultPassed through to the container

privileged, cgroup: host and the /sys/fs/cgroup volume are required; the rest are hardening. Docker deployment explains each one.

Point the service at an image built from your Dockerfile (for example, one from the Dockerfile builder):

services:
faber:
image: my-registry/faber-judge:2026-10-18
build:
context: .
dockerfile: Dockerfile
environment:
- FABER_IMAGE_ID=my-registry/faber-judge:2026-10-18

The default file store lives in memory. For the filesystem backend, mount a volume, because the container’s root is read-only:

services:
faber:
environment:
- FABER_STORE_BACKEND=filesystem
- FABER_STORE_PATH=/var/lib/faber/store
volumes:
- /sys/fs/cgroup:/sys/fs/cgroup:rw
- faber-store:/var/lib/faber/store
volumes:
faber-store:

docker/prod/docker-compose.cluster.yaml runs two Faber services and one Redis on one host. It exists to try the shared step cache and as a starting point; for real scale, run one Faber per host (see running a cluster).

Terminal window
export API_KEY="$(openssl rand -hex 32)"
docker compose -f docker/prod/docker-compose.cluster.yaml up -d

What the file sets up:

  • Redis on the Compose network only, no published port, used purely as a cache.
  • Two Faber services, each with the same image and limits and CACHE_ENABLED=true, CACHE_BACKEND=redis, CACHE_REDIS_URL=redis://redis:6379/0, and the same CACHE_NAMESPACE, so a step one replica caches is a hit on the other.
  • Separate sandbox identity ranges. The two Faber processes share the host kernel, so each needs its own SANDBOX_IDENTITY_BASE (for example 100000 and 200000 with SANDBOX_IDENTITY_COUNT=65536).
  • Separate published ports, so you can address each replica and see a layer stored by one replayed by the other.

Size the host for both replicas: each reserves memory for its own MAX_CONCURRENCY tasks.