Skip to main content
Cerbos provides official Docker images for easy deployment in containerized environments.

Docker Images

Official Cerbos images are available on GitHub Container Registry:
  • Image: ghcr.io/cerbos/cerbos
  • Tags: Version tags (e.g., 0.52.0), latest, dev
  • Architecture: AMD64 and ARM64

Quick Start

Basic Container

Run Cerbos with default configuration:
docker run -d \
  --name cerbos \
  -p 3592:3592 \
  -p 3593:3593 \
  ghcr.io/cerbos/cerbos:latest

With Custom Policies

Mount a local policies directory:
docker run -d \
  --name cerbos \
  -p 3592:3592 \
  -p 3593:3593 \
  -v $(pwd)/policies:/policies \
  -e CERBOS_CONFIG=__default__ \
  ghcr.io/cerbos/cerbos:latest

With Custom Configuration

Mount a configuration file:
docker run -d \
  --name cerbos \
  -p 3592:3592 \
  -p 3593:3593 \
  -v $(pwd)/config/.cerbos.yaml:/config/.cerbos.yaml \
  ghcr.io/cerbos/cerbos:latest server --config=/config/.cerbos.yaml

Dockerfile Details

The official Cerbos Dockerfile:
FROM alpine:3.16 AS base
RUN apk add -U --no-cache ca-certificates && update-ca-certificates

FROM scratch
ARG TARGETPLATFORM
EXPOSE 3592 3593
ENV CERBOS_CONFIG="__default__"
VOLUME ["/policies", "/tmp", "/.cache"]
ENTRYpoint ["/cerbos"]
CMD ["server"]
HEALTHCHECK --interval=10s --timeout=2s --retries=2 CMD ["/cerbos", "healthcheck"]
COPY --from=base /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY ${TARGETPLATFORM}/cerbos /cerbos
Key features:
  • Minimal scratch-based image for security
  • Multi-architecture support (AMD64, ARM64)
  • Built-in health check every 10 seconds
  • Exposed ports: 3592 (HTTP), 3593 (gRPC)
  • Default volumes for policies, temp, and cache

Ports and Volumes

Exposed Ports

  • 3592: HTTP API (REST endpoints, health checks, metrics)
  • 3593: gRPC API (recommended for production)

Volumes

  • /policies: Policy files directory
  • /tmp: Temporary files and caches
  • /.cache: Bundle cache directory (for Hub/remote bundles)

Environment Variables

Default Configuration

When CERBOS_CONFIG=__default__, Cerbos uses:
server:
  httpListenAddr: ":3592"
  grpcListenAddr: ":3593"

storage:
  driver: "disk"
  disk:
    directory: /policies
    watchForChanges: false

Common Variables

CERBOS_CONFIG=__default__        # Use default config
CERBOS_LOG_LEVEL=info           # Log level: debug, info, warn, error

Docker Compose

Basic Setup

Create docker-compose.yml:
services:
  cerbos:
    image: ghcr.io/cerbos/cerbos:latest
    container_name: cerbos
    restart: unless-stopped
    ports:
      - "3592:3592"
      - "3593:3593"
    volumes:
      - ./policies:/policies
      - ./config/.cerbos.yaml:/config/.cerbos.yaml
    command: ["server", "--config=/config/.cerbos.yaml"]
    healthcheck:
      test: ["/cerbos", "healthcheck"]
      interval: 10s
      timeout: 2s
      retries: 3
Start the service:
docker-compose up -d

Production Setup

A production-ready configuration with monitoring:
services:
  cerbos:
    image: ghcr.io/cerbos/cerbos:0.52.0
    container_name: cerbos
    restart: always
    ports:
      - "3592:3592"
      - "3593:3593"
    volumes:
      - ./config:/conf:ro
      - ./policies:/policies:ro
      - ./audit:/audit
      - cerbos-cache:/.cache
    command:
      - "server"
      - "--config=/conf/.cerbos.yaml"
      - "--log-level=info"
    environment:
      - AUDIT_ENABLED=true
      - SCHEMA_ENFORCEMENT=reject
    deploy:
      resources:
        limits:
          cpus: '2.0'
          memory: 512M
        reservations:
          cpus: '0.5'
          memory: 128M
    healthcheck:
      test: ["/cerbos", "healthcheck"]
      interval: 10s
      timeout: 2s
      retries: 3
      start_period: 5s
    networks:
      - cerbos-network

  prometheus:
    image: prom/prometheus:latest
    container_name: prometheus
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
      - prometheus-data:/prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
    networks:
      - cerbos-network
    depends_on:
      - cerbos

  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    ports:
      - "3000:3000"
    volumes:
      - ./grafana/dashboards.yaml:/etc/grafana/provisioning/dashboards/dashboards.yaml:ro
      - ./grafana/datasources.yaml:/etc/grafana/provisioning/datasources/datasources.yaml:ro
      - ./grafana/dashboards:/var/lib/grafana/dashboards:ro
      - grafana-data:/var/lib/grafana
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin
    networks:
      - cerbos-network
    depends_on:
      - prometheus

volumes:
  cerbos-cache:
  prometheus-data:
  grafana-data:

networks:
  cerbos-network:
    driver: bridge
Prometheus configuration (prometheus/prometheus.yml):
global:
  scrape_interval: 15s
  evaluation_interval: 15s

scrape_configs:
  - job_name: 'cerbos'
    static_configs:
      - targets: ['cerbos:3592']
    metrics_path: '/_cerbos/metrics'

With Database Storage

Using PostgreSQL for policy storage:
services:
  cerbos:
    image: ghcr.io/cerbos/cerbos:latest
    container_name: cerbos
    restart: always
    ports:
      - "3592:3592"
      - "3593:3593"
    volumes:
      - ./config:/conf:ro
    command: ["server", "--config=/conf/.cerbos.yaml"]
    environment:
      - DB_HOST=postgres
      - DB_PORT=5432
      - DB_USER=cerbos
      - DB_PASSWORD=cerbos
      - DB_NAME=cerbos
    depends_on:
      postgres:
        condition: service_healthy
    networks:
      - cerbos-network

  postgres:
    image: postgres:14-alpine
    container_name: postgres
    restart: always
    environment:
      - POSTGRES_USER=cerbos
      - POSTGRES_PASSWORD=cerbos
      - POSTGRES_DB=cerbos
    volumes:
      - postgres-data:/var/lib/postgresql/data
      - ./postgres/init.sql:/docker-entrypoint-initdb.d/init.sql:ro
    ports:
      - "5432:5432"
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U cerbos"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - cerbos-network

volumes:
  postgres-data:

networks:
  cerbos-network:
    driver: bridge
Configuration file for PostgreSQL (config/.cerbos.yaml):
server:
  httpListenAddr: ":3592"
  grpcListenAddr: ":3593"

storage:
  driver: "postgres"
  postgres:
    url: "postgresql://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}?sslmode=disable"

Configuration

Storage Backends

server:
  httpListenAddr: ":3592"
  grpcListenAddr: ":3593"

storage:
  driver: "disk"
  disk:
    directory: /policies
    watchForChanges: true

TLS Configuration

Enable TLS for secure communication:
server:
  httpListenAddr: ":3592"
  grpcListenAddr: ":3593"
  tls:
    cert: /certs/server.crt
    key: /certs/server.key
    caCert: /certs/ca.crt
Mount certificates:
docker run -d \
  --name cerbos \
  -p 3592:3592 \
  -p 3593:3593 \
  -v $(pwd)/certs:/certs:ro \
  -v $(pwd)/config/.cerbos.yaml:/config/.cerbos.yaml:ro \
  ghcr.io/cerbos/cerbos:latest server --config=/config/.cerbos.yaml

Audit Logging

Enable audit logs:
audit:
  enabled: true
  backend: "file"
  file:
    path: /audit/audit.log
    retentionPeriod: 168h  # 7 days
Mount audit directory:
docker run -d \
  --name cerbos \
  -p 3592:3592 \
  -p 3593:3593 \
  -v $(pwd)/audit:/audit \
  -v $(pwd)/config/.cerbos.yaml:/config/.cerbos.yaml:ro \
  ghcr.io/cerbos/cerbos:latest server --config=/config/.cerbos.yaml

Health Checks

Built-in Health Check

The Docker image includes a health check:
HEALTHCHECK --interval=10s --timeout=2s --retries=2 \
  CMD ["/cerbos", "healthcheck"]

Manual Health Check

# Check container health
docker inspect --format='{{.State.Health.Status}}' cerbos

# Using curl
docker exec cerbos /cerbos healthcheck

# HTTP endpoint
curl http://localhost:3592/_cerbos/health
Expected response:
{"status":"SERVING"}

Resource Limits

Using Docker Run

docker run -d \
  --name cerbos \
  --memory="512m" \
  --cpus="1.0" \
  --pids-limit=100 \
  -p 3592:3592 \
  -p 3593:3593 \
  ghcr.io/cerbos/cerbos:latest

Using Docker Compose

services:
  cerbos:
    image: ghcr.io/cerbos/cerbos:latest
    deploy:
      resources:
        limits:
          cpus: '2.0'
          memory: 512M
        reservations:
          cpus: '0.5'
          memory: 128M

Networking

Bridge Network (Default)

docker network create cerbos-net

docker run -d \
  --name cerbos \
  --network cerbos-net \
  -p 3592:3592 \
  -p 3593:3593 \
  ghcr.io/cerbos/cerbos:latest

Host Network

For maximum performance:
docker run -d \
  --name cerbos \
  --network host \
  ghcr.io/cerbos/cerbos:latest
Host networking is only available on Linux and removes network isolation.

Security Best Practices

Run as Non-Root User

The Cerbos image runs as a non-root user by default (scratch-based image).

Read-Only Root Filesystem

docker run -d \
  --name cerbos \
  --read-only \
  --tmpfs /tmp:rw,noexec,nosuid,size=100m \
  -p 3592:3592 \
  -p 3593:3593 \
  -v $(pwd)/policies:/policies:ro \
  ghcr.io/cerbos/cerbos:latest

Security Options

docker run -d \
  --name cerbos \
  --cap-drop=ALL \
  --security-opt=no-new-privileges:true \
  --read-only \
  -p 3592:3592 \
  -p 3593:3593 \
  ghcr.io/cerbos/cerbos:latest

Docker Compose Security

services:
  cerbos:
    image: ghcr.io/cerbos/cerbos:latest
    read_only: true
    security_opt:
      - no-new-privileges:true
    cap_drop:
      - ALL
    tmpfs:
      - /tmp:rw,noexec,nosuid,size=100m
    volumes:
      - ./policies:/policies:ro

Monitoring

Container Stats

# Real-time stats
docker stats cerbos

# Logs
docker logs -f cerbos

# Specific time range
docker logs --since 1h cerbos

Prometheus Metrics

Metrics are exposed at http://localhost:3592/_cerbos/metrics:
curl http://localhost:3592/_cerbos/metrics
Key metrics:
  • cerbos_check_duration_seconds: Authorization check duration
  • cerbos_check_total: Total authorization checks
  • cerbos_policy_repo_load_duration_seconds: Policy load time

Troubleshooting

Container Won’t Start

# Check logs
docker logs cerbos

# Inspect container
docker inspect cerbos

# Run interactively
docker run -it --rm \
  -v $(pwd)/config:/config \
  ghcr.io/cerbos/cerbos:latest server --config=/config/.cerbos.yaml

Policy Errors

# Validate policies before mounting
docker run --rm \
  -v $(pwd)/policies:/policies \
  ghcr.io/cerbos/cerbosctl:latest compile /policies

# Check policy loading
docker logs cerbos | grep -i policy

Permission Issues

# Check volume permissions
ls -la policies/

# Fix permissions
chmod -R 755 policies/
chmod 644 policies/*.yaml

Port Conflicts

# Check if ports are in use
lsof -i :3592
lsof -i :3593

# Use different ports
docker run -d \
  --name cerbos \
  -p 13592:3592 \
  -p 13593:3593 \
  ghcr.io/cerbos/cerbos:latest

Complete Production Example

A complete production setup with all best practices:
services:
  cerbos:
    image: ghcr.io/cerbos/cerbos:0.52.0
    container_name: cerbos-prod
    restart: always
    hostname: cerbos
    
    # Ports
    ports:
      - "3592:3592"
      - "3593:3593"
    
    # Volumes
    volumes:
      - ./config/.cerbos.yaml:/config/.cerbos.yaml:ro
      - ./policies:/policies:ro
      - ./certs:/certs:ro
      - ./audit:/audit
      - cerbos-cache:/.cache
    
    # Command
    command:
      - "server"
      - "--config=/config/.cerbos.yaml"
      - "--log-level=info"
    
    # Environment
    environment:
      - AUDIT_ENABLED=true
      - SCHEMA_ENFORCEMENT=reject
    
    # Security
    read_only: true
    security_opt:
      - no-new-privileges:true
    cap_drop:
      - ALL
    tmpfs:
      - /tmp:rw,noexec,nosuid,size=100m
    
    # Resources
    deploy:
      resources:
        limits:
          cpus: '2.0'
          memory: 512M
        reservations:
          cpus: '0.5'
          memory: 128M
      restart_policy:
        condition: on-failure
        delay: 5s
        max_attempts: 3
        window: 120s
    
    # Health check
    healthcheck:
      test: ["/cerbos", "healthcheck"]
      interval: 10s
      timeout: 2s
      retries: 3
      start_period: 5s
    
    # Logging
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"
    
    # Network
    networks:
      - cerbos-network

volumes:
  cerbos-cache:
    driver: local

networks:
  cerbos-network:
    driver: bridge
    ipam:
      config:
        - subnet: 172.28.0.0/16
This configuration provides:
  • Automatic restarts and health checks
  • Resource limits and security hardening
  • Persistent cache and audit logs
  • Proper logging and monitoring
  • Network isolation

Build docs developers (and LLMs) love