Docker Development & Production¶
MIDINecromancer includes Docker Compose configurations for both development and production environments with migration gating and hot reload support.
Overview¶
The Docker setup uses Compose profiles to separate dev and prod environments:
- Dev profile: Hot reload, bind mounts, fast iteration
- Prod profile: Immutable images, static frontend, production server
Both profiles include: - PostgreSQL database with persistent volume - Migration gating (backend starts only after migrations succeed) - Health checks and proper service dependencies
Prerequisites¶
- Docker Engine 20.10+
- Docker Compose v2.0+
.envfile (copy from.env.example)
Quick Start¶
Development¶
# Start all dev services
make docker-dev
# or
docker compose --profile dev up --build
This starts: 1. db: PostgreSQL (healthy check) 2. migrate-dev: Runs Alembic migrations (one-shot) 3. backend-dev: FastAPI with hot reload (waits for migrations) 4. frontend-dev: Vite dev server (waits for backend)
Access: - Frontend: http://localhost:5173 - Backend API: http://localhost:8000 - API Docs: http://localhost:8000/docs
Production¶
# Build and start production services
make docker-prod
# or
docker compose --profile prod up --build -d
This starts: 1. db: PostgreSQL 2. migrate: Runs migrations (one-shot) 3. backend: Gunicorn + Uvicorn workers (waits for migrations) 4. frontend: Nginx serving static assets (waits for backend)
Access: - Frontend: http://localhost:80 - Backend API: http://localhost:8000
Migration Gating¶
Both dev and prod profiles implement migration gating:
- Database must be healthy (
service_healthy) - Migration service must complete successfully (
service_completed_successfully) - Backend starts only after both conditions are met
This ensures: - No backend starts with outdated schema - Migrations are idempotent (safe to run multiple times) - Clear failure if migrations fail
How It Works¶
backend-dev:
depends_on:
db:
condition: service_healthy
migrate-dev:
condition: service_completed_successfully
If migrations fail, backend-dev will not start, preventing schema mismatches.
Development Profile¶
Services¶
- db: PostgreSQL 16 Alpine
- Port: 5432 (exposed for local tools)
- Volume:
postgres_data(persistent) -
Health check:
pg_isready -
migrate-dev: One-shot migration runner
- Runs:
uv run alembic upgrade head - Exits after completion
-
Source bind-mounted for live changes
-
backend-dev: FastAPI with hot reload
- Command:
uvicorn --reload - Port: 8000
- Source bind-mounted:
./backend:/app -
Auto-restarts on code changes
-
frontend-dev: Vite dev server
- Port: 5173
- Source bind-mounted:
./frontend:/app - Hot Module Replacement (HMR) enabled
Hot Reload¶
Both backend and frontend support hot reload via bind mounts:
- Backend: Uvicorn
--reloadwatches for Python file changes - Frontend: Vite HMR watches for Vue/TypeScript changes
Edit files locally, see changes immediately in containers.
Environment Variables¶
Set in .env:
POSTGRES_USER=midinecromancer
POSTGRES_PASSWORD=midinecromancer
POSTGRES_DB=midinecromancer
BACKEND_PORT=8000
FRONTEND_PORT=5173
DATABASE_URL=postgresql+asyncpg://midinecromancer:midinecromancer@db:5432/midinecromancer
Production Profile¶
Services¶
- db: Same PostgreSQL setup
- Port: Not exposed (internal only)
-
Volume:
postgres_data -
migrate: One-shot migration runner
- Uses production backend image
- Runs:
alembic upgrade head -
No source mounts
-
backend: Production server
- Gunicorn + 4 Uvicorn workers
- Port: 8000 (internal, proxied by frontend)
- No source mounts (immutable)
-
Health check enabled
-
frontend: Static asset server
- Nginx Alpine
- Serves built
dist/directory - SPA routing (history mode fallback)
- Proxies
/apito backend - Port: 80
Build Process¶
Production images are built with:
- Backend: Non-editable install (uv sync --no-dev)
- Frontend: npm run build → static assets → nginx
No development dependencies or source code in production images.
Dockerfiles¶
Backend Dockerfile¶
Multi-stage with dev and prod targets:
Dev target:
- Installs uv
- uv sync (includes dev deps)
- Bind-mount friendly
- Runs uvicorn --reload
Prod target:
- uv sync --no-dev (production deps only)
- Non-editable install
- Gunicorn + Uvicorn workers
- Non-root user
- Health check
Frontend Dockerfile¶
Multi-stage with dev, build, and prod targets:
Dev target:
- Node 20 Alpine
- npm ci
- Runs Vite dev server
Build stage:
- npm run build
- Outputs to dist/
Prod target:
- Nginx Alpine
- Serves dist/
- SPA routing config
- API proxy to backend
Common Commands¶
Development¶
# Start dev environment
make docker-dev
# View logs
make docker-logs
# or
docker compose --profile dev logs -f
# Stop services
make docker-down
# or
docker compose --profile dev down
# Clean volumes (removes database data)
make docker-clean
Production¶
# Start production
make docker-prod
# View logs
docker compose --profile prod logs -f
# Stop services
docker compose --profile prod down
# Rebuild after code changes
docker compose --profile prod up --build -d
Database¶
# Connect to database
docker compose exec db psql -U midinecromancer -d midinecromancer
# Run migrations manually (dev)
docker compose --profile dev run --rm migrate-dev
# Run migrations manually (prod)
docker compose --profile prod run --rm migrate
Troubleshooting¶
Migrations Fail¶
If migrations fail:
1. Check logs: docker compose logs migrate-dev (or migrate)
2. Backend will not start (by design)
3. Fix migration issues, then restart
Backend Won't Start¶
Check dependencies:
# Is database healthy?
docker compose ps db
# Did migrations complete?
docker compose logs migrate-dev
Port Conflicts¶
Change ports in .env:
BACKEND_PORT=8001
FRONTEND_PORT=5174
POSTGRES_PORT=5433
Hot Reload Not Working¶
Ensure bind mounts are correct:
docker compose --profile dev config | grep volumes
Should show ./backend:/app and ./frontend:/app.
Local Development Without Docker¶
Docker is optional. You can still run locally:
# Backend
cd backend
uv sync
uv run uvicorn midinecromancer.main:app --reload
# Frontend
cd frontend
npm install
npm run dev
Just ensure PostgreSQL is running (via Docker or local install).
Best Practices¶
- Use
.envfile: Never commit secrets - Check migrations: Always verify migrations complete before backend starts
- Rebuild after dependency changes: Run
--buildwhenpyproject.tomlorpackage.jsonchange - Clean volumes carefully:
docker-cleanremoves database data - Use health checks: Services wait for dependencies to be healthy
Production Deployment¶
For production deployment:
- Set strong passwords in
.env - Use secrets management (Docker secrets, env files, etc.)
- Configure CORS origins properly
- Set
ENVIRONMENT=productionandDEBUG=false - Use reverse proxy (nginx/traefik) in front of compose services
- Enable SSL/TLS
- Set resource limits in compose file
- Use Docker secrets for sensitive data
Image Sizes¶
Approximate sizes: - backend-dev: ~500MB (includes dev tools) - backend-prod: ~200MB (production only) - frontend-dev: ~200MB (Node + dependencies) - frontend-prod: ~50MB (nginx + static assets)
Production images are optimized for size and security.