Skip to content

Docker Compose

  • Docker Engine 20.10+
  • Docker Compose v2+

A complete Sonarr deployment with PostgreSQL, PrepArr init, the Sonarr app, and the PrepArr sidecar:

services:
postgres:
image: postgres:16-alpine
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres123
POSTGRES_DB: servarr
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
sonarr-init:
image: ghcr.io/robbeverhelst/preparr:latest
command: ["bun", "run", "dist/index.js", "--init"]
environment:
POSTGRES_HOST: postgres
POSTGRES_PASSWORD: postgres123
SERVARR_URL: http://sonarr:8989
SERVARR_TYPE: sonarr
SERVARR_ADMIN_PASSWORD: adminpass
CONFIG_PATH: /config/sonarr-config.json
volumes:
- sonarr_config:/config
- ./sonarr-config.json:/config/sonarr-config.json:ro
depends_on:
postgres:
condition: service_healthy
sonarr:
image: linuxserver/sonarr:latest
ports:
- "8989:8989"
environment:
- PUID=1000
- PGID=1000
- TZ=UTC
volumes:
- sonarr_config:/config
- ./tv:/tv
- ./downloads:/downloads
depends_on:
sonarr-init:
condition: service_completed_successfully
sonarr-sidecar:
image: ghcr.io/robbeverhelst/preparr:latest
environment:
POSTGRES_HOST: postgres
POSTGRES_PASSWORD: postgres123
SERVARR_URL: http://sonarr:8989
SERVARR_TYPE: sonarr
SERVARR_ADMIN_PASSWORD: adminpass
CONFIG_PATH: /config/sonarr-config.json
CONFIG_WATCH: "true"
CONFIG_RECONCILE_INTERVAL: "60"
HEALTH_PORT: "9001"
LOG_LEVEL: info
ports:
- "9001:9001"
volumes:
- sonarr_config:/config
- ./sonarr-config.json:/config/sonarr-config.json:ro
depends_on:
- sonarr
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9001/health"]
interval: 30s
timeout: 10s
retries: 3
volumes:
postgres_data:
sonarr_config:
ContainerPurposeLifecycle
postgresShared databasePersistent, always running
sonarr-initDatabase setup, config.xml generationRuns once, then exits
sonarrThe Sonarr applicationAlways running
sonarr-sidecarConfiguration reconciliationAlways running alongside Sonarr

Docker Compose depends_on conditions enforce the correct order:

  1. PostgreSQL starts and passes its health check
  2. Init container runs, creates databases and config.xml, then exits
  3. Sonarr starts using the prepared config
  4. Sidecar starts and begins reconciling configuration

For a full Prowlarr + Sonarr + Radarr + qBittorrent stack, see the Multi-Service Stack guide.

Terminal window
# Start all services
docker compose up -d
# View sidecar logs
docker compose logs -f sonarr-sidecar
# Restart sidecar after config changes
docker compose restart sonarr-sidecar
# Stop everything
docker compose down
# Stop and remove volumes (full reset)
docker compose down -v

If CONFIG_WATCH=true (default), the sidecar detects config file changes automatically. Otherwise:

  1. Edit your JSON config file
  2. Restart the sidecar: docker compose restart sonarr-sidecar
DataStorageNotes
PostgreSQL dataNamed volume (postgres_data)Survives container restarts
Config files (JSON)Bind mount from hostRead-only, version controlled
config.xmlNamed volume (sonarr_config)Regenerated by init container
Media filesBind mount from hostSonarr reads/writes directly

PrepArr is stateless. If you lose the sonarr_config volume, run the init container again to regenerate everything.