Wine Cellar
Self-hosted wine management app for tracking your collection and tasting notes
Alternative to: vivino, cellartracker
Wine Cellar is a self-hosted wine management app built with Django for wine enthusiasts to track wines, store tasting notes, rate bottles, and manage inventory. It supports multi-user hosting, barcode scanning to add or remove bottles, food pairing notes, and email reminders for wines nearing their drink-by date.
Wine Cellar Docker Compose example
Self-host Wine Cellar on your own server, homelab, or VPS starting from this Docker Compose example.
It runs Wine Cellar in Docker containers using the official ghcr.io/the-broke-sommeliers/wine-cellar-web:latest, postgres:16, redis:8-alpine, caddy:2, ghcr.io/the-broke-sommeliers/wine-cellar-web:latest, ghcr.io/the-broke-sommeliers/wine-cellar-web:latest images, with persistent volumes and automatic restarts preconfigured.
Review the environment variables and adjust them to your setup, save the file as compose.yml (or docker-compose.yml), and start the stack with docker compose up -d.
services:
web:
image: ghcr.io/the-broke-sommeliers/wine-cellar-web:latest
restart: unless-stopped
command: gunicorn wine_cellar.conf.wsgi:application --bind 0.0.0.0:8000 --worker-class gthread --threads 4 --workers 2
volumes:
- static_volume:/home/app/web/staticfiles
- media_volume:/home/app/web/mediafiles
environment:
# Marks this container as the web role (runs migrations, collectstatic, admin bootstrap on start).
CONTAINER_ROLE: "web"
DJANGO_DEBUG: "False"
# Django secret key used for cryptographic signing. Set to a long random string.
SECRET_KEY: "changeme"
# Space-separated hostnames the app will serve (add your public domain).
DJANGO_ALLOWED_HOSTS: "localhost 127.0.0.1 [::1]"
# Space-separated trusted origins for CSRF (include your public https URL).
DJANGO_CSRF_TRUSTED_ORIGINS: "http://127.0.0.1:8085"
# Public URL of the site, used in emails and links.
DJANGO_SITE_URL: "https://example.org"
# Extra hosts to allow in the form-action Content-Security-Policy directive.
DJANGO_CSP_FORM_ACTION_EXTRA: ""
SQL_ENGINE: "django.db.backends.postgresql"
# Name of the PostgreSQL database.
SQL_DATABASE: "django"
# PostgreSQL username.
SQL_USER: "django"
# PostgreSQL password.
SQL_PASSWORD: "changeme"
SQL_HOST: "db"
SQL_PORT: "5432"
DATABASE: "postgres"
REDIS_URL: "redis://redis:6379"
DJANGO_SETTINGS_MODULE: "wine_cellar.conf.docker_settings"
# Username of the initial admin account created on first start.
ADMIN_USER: "admin"
# Email of the initial admin account.
ADMIN_USER_EMAIL: "admin@example.org"
# Password of the initial admin account.
ADMIN_USER_PASSWORD: "changeme"
# SMTP server hostname for outgoing email (leave empty to disable email).
DJANGO_EMAIL_HOST: ""
# SMTP server port.
DJANGO_EMAIL_PORT: ""
# SMTP username.
DJANGO_EMAIL_USER: ""
# SMTP password.
DJANGO_EMAIL_PASSWORD: ""
# Use STARTTLS for SMTP (set to True to enable; mutually exclusive with SSL).
DJANGO_EMAIL_USE_TLS: ""
# Use implicit SSL for SMTP (set to True to enable; mutually exclusive with TLS).
DJANGO_EMAIL_USE_SSL: ""
# Default From address for outgoing email.
DJANGO_DEFAULT_FROM_EMAIL: ""
# Sentry DSN for error reporting (optional).
SENTRY_DSN: ""
# Allow new users to register.
DJANGO_ENABLE_SIGNUPS: "False"
# Allow registration via social login providers.
DJANGO_ENABLE_SOCIAL_SIGNUPS: "True"
# Show the "What's new" changelog panel.
DJANGO_ENABLE_WHATS_NEW: "True"
# Email verification for password signups: mandatory, optional or none.
DJANGO_ACCOUNT_EMAIL_VERIFICATION: "optional"
# Email verification for social signups: mandatory, optional or none.
DJANGO_SOCIALACCOUNT_EMAIL_VERIFICATION: "optional"
DJANGO_SOCIALACCOUNT_EMAIL_AUTHENTICATION: "False"
DJANGO_SOCIALACCOUNT_EMAIL_AUTHENTICATION_AUTO_CONNECT: "False"
# LiteLLM model string for AI wine-label scanning (e.g. gemini/gemini-2.5-flash).
AI_MODEL: ""
# API key for the AI model provider.
AI_API_KEY: ""
depends_on:
db:
condition: service_started
redis:
condition: service_started
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health/"]
interval: 10s
timeout: 5s
retries: 5
start_period: 20s
db:
image: postgres:16
restart: unless-stopped
volumes:
- postgres_data:/var/lib/postgresql/data/
environment:
# PostgreSQL username.
POSTGRES_USER: "django"
# PostgreSQL password.
POSTGRES_PASSWORD: "changeme"
# Name of the PostgreSQL database.
POSTGRES_DB: "django"
redis:
image: redis:8-alpine
restart: unless-stopped
caddy:
image: caddy:2
restart: unless-stopped
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- static_volume:/srv/static
- media_volume:/srv/media
- caddy_data:/data
- caddy_config:/config
depends_on:
- web
celery:
image: ghcr.io/the-broke-sommeliers/wine-cellar-web:latest
restart: unless-stopped
command: celery -A wine_cellar.conf worker -l info
environment:
DJANGO_DEBUG: "False"
# Django secret key used for cryptographic signing. Set to a long random string.
SECRET_KEY: "changeme"
# Space-separated hostnames the app will serve (add your public domain).
DJANGO_ALLOWED_HOSTS: "localhost 127.0.0.1 [::1]"
SQL_ENGINE: "django.db.backends.postgresql"
# Name of the PostgreSQL database.
SQL_DATABASE: "django"
# PostgreSQL username.
SQL_USER: "django"
# PostgreSQL password.
SQL_PASSWORD: "changeme"
SQL_HOST: "db"
SQL_PORT: "5432"
DATABASE: "postgres"
REDIS_URL: "redis://redis:6379"
DJANGO_SETTINGS_MODULE: "wine_cellar.conf.docker_settings"
# SMTP server hostname for outgoing email (leave empty to disable email).
DJANGO_EMAIL_HOST: ""
# SMTP server port.
DJANGO_EMAIL_PORT: ""
# SMTP username.
DJANGO_EMAIL_USER: ""
# SMTP password.
DJANGO_EMAIL_PASSWORD: ""
# Use STARTTLS for SMTP (set to True to enable; mutually exclusive with SSL).
DJANGO_EMAIL_USE_TLS: ""
# Use implicit SSL for SMTP (set to True to enable; mutually exclusive with TLS).
DJANGO_EMAIL_USE_SSL: ""
# Default From address for outgoing email.
DJANGO_DEFAULT_FROM_EMAIL: ""
depends_on:
web:
condition: service_healthy
redis:
condition: service_started
celery-beat:
image: ghcr.io/the-broke-sommeliers/wine-cellar-web:latest
restart: unless-stopped
command: celery -A wine_cellar.conf beat -l info
environment:
DJANGO_DEBUG: "False"
# Django secret key used for cryptographic signing. Set to a long random string.
SECRET_KEY: "changeme"
# Space-separated hostnames the app will serve (add your public domain).
DJANGO_ALLOWED_HOSTS: "localhost 127.0.0.1 [::1]"
SQL_ENGINE: "django.db.backends.postgresql"
# Name of the PostgreSQL database.
SQL_DATABASE: "django"
# PostgreSQL username.
SQL_USER: "django"
# PostgreSQL password.
SQL_PASSWORD: "changeme"
SQL_HOST: "db"
SQL_PORT: "5432"
DATABASE: "postgres"
REDIS_URL: "redis://redis:6379"
DJANGO_SETTINGS_MODULE: "wine_cellar.conf.docker_settings"
depends_on:
web:
condition: service_healthy
redis:
condition: service_started
volumes:
postgres_data:
static_volume:
media_volume:
caddy_data:
caddy_config:
Values set to changeme are required — replace them with your own
values before starting Wine Cellar.
Extra files
The Docker Compose configuration above bind-mounts the following file. Save it next to your compose.yml,
keeping the same relative path.
Caddyfile
:80 {
@files {
path /static/* /media/*
}
handle @files {
file_server
}
handle {
@health path /health
respond @health "Not Found" 404
reverse_proxy web:8000
}
} Prefer a managed setup? WinterFlow installs, configures, and updates Wine Cellar for you using this same Docker Compose configuration.