All configuration is done through environment variables passed to the Docker container, plus a few in-app settings.
Black Candy Store can handle HTTPS natively with automatic Let's Encrypt certificates — no reverse proxy needed.
docker run -p 80:80 -p 443:443 \
-e TLS_DOMAIN=music.example.com \
-v ./storage_data:/rails/storage \
ghcr.io/ajeskey/blackcandystore:latest
Requirements:
/rails/storage volume must be persisted (certificates are cached there)Certificates are provisioned on first request and renewed automatically. You can pass multiple domains as a comma-separated list.
If you prefer to terminate TLS at a reverse proxy (Caddy, Nginx, Traefik), leave TLS_DOMAIN unset and configure your proxy to forward traffic to the container's HTTP port.
SQLite is the default and works great for personal use. For larger deployments or cloud hosting, switch to PostgreSQL.
No configuration needed. The database file is stored in /rails/storage.
docker run \
-e DB_ADAPTER=postgresql \
-e DB_URL=postgresql://user:pass@host:5432/blackcandystore \
-e CABLE_DB_URL=postgresql://user:pass@host:5432/blackcandystore_cable \
-e QUEUE_DB_URL=postgresql://user:pass@host:5432/blackcandystore_queue \
-e CACHE_DB_URL=postgresql://user:pass@host:5432/blackcandystore_cache \
-p 80:80 \
ghcr.io/ajeskey/blackcandystore:latest
Some settings are managed through the web UI rather than environment variables. To access them:
The Settings page has the following sections:
At the top of the Settings page:
MEDIA_PATH env var)Below the Library section:
Each section has its own Save button — make sure to click it after changing values.
Used for encrypting sessions and security-sensitive data. Generate one with:
openssl rand -hex 64
Then pass it to the container:
docker run -e SECRET_KEY_BASE=your_generated_secret -p 80:80 ghcr.io/ajeskey/blackcandystore:latest
If SECRET_KEY_BASE is not set, a new one is generated on each startup, which invalidates all existing sessions.
Cross-server sharing uses Active Record encryption to protect access tokens at rest. Set these three variables in production:
AR_ENCRYPTION_PRIMARY_KEYAR_ENCRYPTION_DETERMINISTIC_KEYAR_ENCRYPTION_KEY_DERIVATION_SALTIf unset, development defaults are used — fine for testing, not for production.
| Variable | Default | Description |
|---|---|---|
MEDIA_PATH |
— | Path to music files inside the container |
DB_ADAPTER |
sqlite |
sqlite or postgresql |
DB_URL |
— | PostgreSQL connection URL |
CABLE_DB_URL |
— | Pub/Sub database URL (PostgreSQL only) |
QUEUE_DB_URL |
— | Background job database URL (PostgreSQL only) |
CACHE_DB_URL |
— | Cache database URL (PostgreSQL only) |
SECRET_KEY_BASE |
random | Secret for sessions and encryption |
TLS_DOMAIN |
— | Domain(s) for automatic HTTPS |
HTTP_PORT |
80 |
Internal port the server listens on |
FORCE_SSL |
false |
Force all access over HTTPS |
SERVER_BASE_URL |
http://localhost:3000 |
Public URL for cross-server sharing |
CATALOG_SYNC_POLL_INTERVAL |
15 |
Minutes between catalog sync pulls |
DEMO_MODE |
false |
Lock down admin access for demos |
AR_ENCRYPTION_PRIMARY_KEY |
— | Primary key for Active Record encryption |
AR_ENCRYPTION_DETERMINISTIC_KEY |
— | Deterministic key for Active Record encryption |
AR_ENCRYPTION_KEY_DERIVATION_SALT |
— | Key-derivation salt for Active Record encryption |
Logs go to STDOUT by default. Use Docker's built-in logging drivers to route them wherever you need:
docker run --log-driver=json-file --log-opt max-size=10m -p 80:80 ghcr.io/ajeskey/blackcandystore:latest