Production-ready Compose files for common deployment scenarios.
The simplest production setup — single container with automatic TLS.
version: "3.8"
services:
blackcandystore:
image: ghcr.io/ajeskey/blackcandystore:latest
ports:
- "80:80"
- "443:443"
volumes:
- ./storage_data:/rails/storage
- /path/to/music:/media_data
environment:
- MEDIA_PATH=/media_data
- SECRET_KEY_BASE=your_64_char_hex_secret
- TLS_DOMAIN=music.example.com
restart: unless-stopped
Mount several music directories and create a library for each in the admin UI.
version: "3.8"
services:
blackcandystore:
image: ghcr.io/ajeskey/blackcandystore:latest
ports:
- "80:80"
- "443:443"
volumes:
- ./storage_data:/rails/storage
- /mnt/nas/jazz:/media/jazz
- /mnt/nas/electronic:/media/electronic
- /mnt/nas/vinyl-rips:/media/vinyl
- /mnt/nas/podcasts:/media/podcasts
environment:
- SECRET_KEY_BASE=your_64_char_hex_secret
- TLS_DOMAIN=music.example.com
restart: unless-stopped
When using multiple libraries, don't set MEDIA_PATH — instead, configure each library's path individually through the admin UI after startup.
For larger collections or multi-user deployments. Includes a dedicated PostgreSQL container.
version: "3.8"
services:
blackcandystore:
image: ghcr.io/ajeskey/blackcandystore:latest
ports:
- "80:80"
- "443:443"
volumes:
- ./storage_data:/rails/storage
- /path/to/music:/media_data
environment:
- MEDIA_PATH=/media_data
- SECRET_KEY_BASE=your_64_char_hex_secret
- TLS_DOMAIN=music.example.com
- DB_ADAPTER=postgresql
- DB_URL=postgresql://blackcandy:secretpassword@db:5432/blackcandystore
- CABLE_DB_URL=postgresql://blackcandy:secretpassword@db:5432/blackcandystore_cable
- QUEUE_DB_URL=postgresql://blackcandy:secretpassword@db:5432/blackcandystore_queue
- CACHE_DB_URL=postgresql://blackcandy:secretpassword@db:5432/blackcandystore_cache
depends_on:
db:
condition: service_healthy
restart: unless-stopped
db:
image: postgres:16-alpine
volumes:
- pgdata:/var/lib/postgresql/data
environment:
- POSTGRES_USER=blackcandy
- POSTGRES_PASSWORD=secretpassword
- POSTGRES_DB=blackcandystore
healthcheck:
test: ["CMD-SHELL", "pg_isready -U blackcandy"]
interval: 5s
timeout: 5s
retries: 5
restart: unless-stopped
volumes:
pgdata:
Full setup with encryption keys for secure cross-server library sharing.
version: "3.8"
services:
blackcandystore:
image: ghcr.io/ajeskey/blackcandystore:latest
ports:
- "80:80"
- "443:443"
volumes:
- ./storage_data:/rails/storage
- /path/to/music:/media_data
environment:
- MEDIA_PATH=/media_data
- SECRET_KEY_BASE=your_64_char_hex_secret
- TLS_DOMAIN=music.example.com
- SERVER_BASE_URL=https://music.example.com
- AR_ENCRYPTION_PRIMARY_KEY=your_primary_key
- AR_ENCRYPTION_DETERMINISTIC_KEY=your_deterministic_key
- AR_ENCRYPTION_KEY_DERIVATION_SALT=your_salt
- CATALOG_SYNC_POLL_INTERVAL=15
restart: unless-stopped
Generate your encryption keys with:
# Generate all three keys
openssl rand -hex 32 # AR_ENCRYPTION_PRIMARY_KEY
openssl rand -hex 32 # AR_ENCRYPTION_DETERMINISTIC_KEY
openssl rand -hex 32 # AR_ENCRYPTION_KEY_DERIVATION_SALT
If you prefer to manage TLS externally with Caddy:
version: "3.8"
services:
blackcandystore:
image: ghcr.io/ajeskey/blackcandystore:latest
expose:
- "80"
volumes:
- ./storage_data:/rails/storage
- /path/to/music:/media_data
environment:
- MEDIA_PATH=/media_data
- SECRET_KEY_BASE=your_64_char_hex_secret
- FORCE_SSL=true
restart: unless-stopped
caddy:
image: caddy:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
restart: unless-stopped
volumes:
caddy_data:
With a Caddyfile:
music.example.com {
reverse_proxy blackcandystore:80
}
For any of the above, generate your SECRET_KEY_BASE with:
openssl rand -hex 64
Never reuse secrets between environments or share them publicly.