Skip to content

Containment Chamber publishes multi-arch Docker images to GitHub Container Registry. The image runs on a scratch base with no shell or OS — just the statically-linked binary.

Start a signer with a local config file and keystores directory:

Terminal window
docker run -d \
-p 9000:9000 \
-p 3000:3000 \
-v ./config.yaml:/config.yaml \
-v ./keystores:/keystores \
ghcr.io/unforeseen-consequences/containment-chamber:latest \
-c /config.yaml

Port 9000 serves the signing API, and port 3000 exposes Prometheus metrics.

Create a docker-compose.yml alongside your config.yaml and keystores/ directory:

services:
containment-chamber:
image: ghcr.io/unforeseen-consequences/containment-chamber:latest
command: -c /config.yaml
ports:
- "9000:9000" # signing API
- "3000:3000" # metrics
volumes:
- ./config.yaml:/config.yaml:ro
- ./keystores:/keystores:ro
environment:
CONTAINMENT_ANTISLASHING__URL: postgresql://cc:secret@postgres:5432/slashing
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://localhost:9000/upcheck || exit 1"]
interval: 10s
timeout: 5s
retries: 3
start_period: 10s
postgres:
image: postgres:16-alpine
environment:
POSTGRES_USER: cc
POSTGRES_PASSWORD: secret
POSTGRES_DB: slashing
volumes:
- postgres-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U cc -d slashing"]
interval: 5s
timeout: 3s
retries: 5
volumes:
postgres-data:

Create a minimal config.yaml that works with the Compose setup above:

server:
listen_address: "0.0.0.0"
listen_port: 9000
metrics:
listen_address: "0.0.0.0"
listen_port: 3000
network: mainnet
key_sources:
filesystem:
paths:
- /keystores
antislashing:
backend: postgres
# URL is injected via CONTAINMENT_ANTISLASHING__URL environment variable

The CONTAINMENT_ANTISLASHING__URL environment variable in the Compose file overrides the config file value, keeping the database credentials out of the config.

All configuration options can be set via environment variables using the CONTAINMENT_ prefix. Use double underscores (__) to represent nesting in the YAML structure.

Terminal window
CONTAINMENT_SERVER__LISTEN_PORT=9001
CONTAINMENT_ANTISLASHING__BACKEND=postgres
CONTAINMENT_ANTISLASHING__URL="postgresql://cc:secret@postgres:5432/slashing"
Terminal window
# Start all services
docker compose up -d
# Watch signer logs
docker compose logs -f containment-chamber
# Check health
curl http://localhost:9000/upcheck
# List loaded validator keys
curl http://localhost:9000/api/v1/eth2/publicKeys
# Stop all services
docker compose down
# Stop and remove volumes (deletes slashing protection database)
docker compose down -v
TagDescription
latestLatest stable release
vX.Y.ZPinned to a specific version

For production, pin to a specific version tag to avoid unexpected upgrades:

Terminal window
docker pull ghcr.io/unforeseen-consequences/containment-chamber:v1.0.0

Images are built for both linux/amd64 and linux/arm64. Docker automatically pulls the correct architecture for your platform — this includes Apple Silicon Macs, AWS Graviton instances, and standard x86 servers.