# DEV stack for mycoolmusicapp — one entrypoint at http://localhost:80. # # make up # build + start everything (db, redis, api, worker, webui, nginx) # make logs # tail logs # make down # stop # # Source is bind-mounted into api/worker/webui, so edits hot-reload without a # rebuild. PROD has NO compose yet — services there are built straight from # their dockerfiles/Dockerfile.prod (a separate prod compose lands later). # # Env: copy .env.example -> .env at the repo root (single combined file). services: # -- backing services ----------------------------------------------------- db: image: postgres:16-alpine environment: POSTGRES_USER: ${POSTGRES_USER:-mcma} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-mcma} POSTGRES_DB: ${POSTGRES_DB:-mcma} ports: - "${POSTGRES_PORT:-5432}:5432" # exposed so host-run tests can reach it volumes: - ./data/pgdata:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-mcma}"] interval: 10s timeout: 5s retries: 5 restart: unless-stopped redis: image: redis:7-alpine command: redis-server --save 60 1 --loglevel warning ports: - "${REDIS_PORT:-6379}:6379" volumes: - ./data/redisdata:/data healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 10s timeout: 5s retries: 5 restart: unless-stopped # -- application services ------------------------------------------------- api: build: context: ./mcma-backend dockerfile: dockerfiles/Dockerfile.dev env_file: .env environment: # Service-name URLs override the localhost values from .env. DATABASE_URL: postgresql+asyncpg://${POSTGRES_USER:-mcma}:${POSTGRES_PASSWORD:-mcma}@db:5432/${POSTGRES_DB:-mcma} REDIS_URL: redis://redis:6379/0 volumes: - ./mcma-backend:/app # live source (hot reload) - /app/.venv # keep the image's venv, don't shadow it - ./data/media:/data/media - ./data/transcode-cache:/data/transcode-cache - ./data/youtube:/data/youtube # yt-dlp cookies.txt (optional; see .env) ports: - "${API_PORT:-8000}:8000" # direct access for debugging / docs depends_on: db: { condition: service_healthy } redis: { condition: service_healthy } restart: unless-stopped worker: build: context: ./mcma-backend dockerfile: dockerfiles/Dockerfile.dev command: arq app.workers.arq_worker.WorkerSettings env_file: .env environment: DATABASE_URL: postgresql+asyncpg://${POSTGRES_USER:-mcma}:${POSTGRES_PASSWORD:-mcma}@db:5432/${POSTGRES_DB:-mcma} REDIS_URL: redis://redis:6379/0 volumes: - ./mcma-backend:/app - /app/.venv - ./data/media:/data/media - ./data/transcode-cache:/data/transcode-cache - ./data/youtube:/data/youtube # yt-dlp cookies.txt (optional; see .env) depends_on: db: { condition: service_healthy } redis: { condition: service_healthy } restart: unless-stopped webui: build: context: ./mcma-webui dockerfile: dockerfiles/Dockerfile.dev env_file: .env environment: RSBUILD_HOST: 0.0.0.0 RSBUILD_PORT: "${WEBUI_PORT:-3000}" # Browser reaches HMR through nginx on :80, not the container's :3000. RSBUILD_HMR_CLIENT_PORT: "${NGINX_PORT:-8881}" volumes: - ./mcma-webui:/app - /app/node_modules # keep the image's install, don't shadow it expose: - "${WEBUI_PORT:-3000}" restart: unless-stopped # -- reverse proxy: single entrypoint at localhost:80 --------------------- nginx: image: nginx:1.27-alpine ports: - "${NGINX_PORT:-8881}:80" volumes: - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro depends_on: - api - webui restart: unless-stopped