Skip to content

Docker Compose

Purpose

Run the full local stack - MySQL, Redis, backend API, frontend - with one command, for day-to-day application development.

Prerequisites

Docker Desktop running. Optionally a .env file at the repo root (copy .env.example) to override any default below - every default is safe to leave as-is for local development.

Files involved

docker-compose.yml (base stack), docker-compose.dev.yml (dev overrides: hot reload, exposed ports), database/init.sql (mounted read-only into MySQL's entrypoint-init directory).

Services (docker-compose.yml)

Service Image Container name Host port(s) Purpose
mysql mysql:8.2 ecommerce_mysql 3309:3306 (configurable via MYSQL_HOST_PORT) Database
redis redis:7.2-alpine ecommerce_redis (not published - only reachable from other Compose services) Cache/session
api built from backend/Dockerfile ecommerce_api (not published in the base file - only docker-compose.dev.yml exposes 5000:8080) Backend
frontend built from frontend/Dockerfile ecommerce_frontend 80:80, 443:443 Single external entry point - serves the SPA and proxies /api/*
adminer adminer:latest ecommerce_adminer 8080:8080 Database UI - only runs with --profile dev, never starts by default

Configuration (environment variables, all with safe local defaults)

Variable Default if unset Used by
MYSQL_ROOT_PASSWORD rootpassword mysql
MYSQL_PASSWORD ecommerce_pass mysql, api
MYSQL_HOST_PORT 3309 mysql
REDIS_PASSWORD redispassword redis, api
JWT_SECRET_KEY a placeholder string (must be overridden before anything resembling production use) api
ASPNETCORE_ENVIRONMENT Production api
FRONTEND_URL http://localhost api (CORS AllowedOrigins)
EMAIL_PROVIDER / WHATSAPP_PROVIDER / PUSH_PROVIDER Simulated api - safe no-op providers; set a real provider name + matching *_API_KEY/*_ACCESS_TOKEN in .env to send real notifications

These fallback defaults only ever apply on a developer's own machine when .env is absent - never in any Kubernetes environment, which requires an explicitly pre-created Secret with no fallback (see Kubernetes → Helm).

Volumes

Volume Mounted at Purpose
mysql_data /var/lib/mysql Database files - persists across docker compose down (not down -v)
redis_data /data Redis AOF persistence
api_uploads /app/uploads Locally-stored file uploads (when FileStorage:Provider=Local)
./database/init.sql /docker-entrypoint-initdb.d/init.sql (read-only) One-time schema/seed on first MySQL start

Networking

All services share one bridge network, ecommerce_network. Services reach each other by service name (Server=mysql;..., redis:6379) - only frontend (80/443) and, in dev mode, api (5000→8080) and adminer (8080) publish host ports.

Health checks

Every service except adminer has a HEALTHCHECK/Compose healthcheck. api and frontend both depends_on their dependencies with condition: service_healthy - frontend won't even attempt to start until api reports healthy, and api won't start until mysql/redis do.

Commands

# Start (background)
docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d --build

# Start (foreground, dev hot-reload)
docker compose -f docker-compose.yml -f docker-compose.dev.yml up --build

# Status
docker compose ps

# Logs (all services, follow)
docker compose logs -f

# Logs (one service)
docker compose logs -f api

# Stop (keeps volumes/data)
docker compose -f docker-compose.yml -f docker-compose.dev.yml down

# Stop AND delete volumes (MySQL/Redis data gone - only do this deliberately)
docker compose -f docker-compose.yml -f docker-compose.dev.yml down -v

# Rebuild one service after a Dockerfile/dependency change
docker compose build api
docker compose up -d api

# Optional: Adminer DB UI
docker compose --profile dev up -d adminer

Validation / expected result

docker compose ps
# every service listed "healthy" or "running (healthy)"
Frontend at http://localhost (or http://localhost:3000 with docker-compose.dev.yml's Vite dev server), backend directly at http://localhost:5000 (dev override only).

Troubleshooting

See Docker → Troubleshooting.

Rollback / recovery

docker compose down never deletes the named volumes (mysql_data, redis_data, api_uploads) unless you pass -v explicitly - restarting with up again reattaches to the same data. To reset to a clean database, down -v then up --build re-runs database/init.sql.

Security considerations

  • docker-compose.override.yml is gitignored - the intended place for any real local secret override, never committed.
  • .env is gitignored (see repository root .gitignore) - never commit a filled-in .env.
  • The weak fallback passwords above are a deliberate, low-severity local-only convenience - documented, not hidden, and never reachable outside docker compose up on your own machine.