---
title: "Docker Compose Fullstack Deployment"
aliases: [docker-compose, docker, deployment, devops]
tags: [docker, docker-compose, deployment, fullstack]
sources: [01 Projects/gmal-scope-builder, 01 Projects/modcomms, 01 Projects/enterprise-ai-hub-nexus, 01 Projects/video-accessibility, 01 Projects/sandbox-notebookllamalm-nextjs]
created: 2026-04-15
updated: 2026-04-15
---

# Docker Compose Fullstack Deployment

The standard Oliver deployment model. Every service (frontend, backend, DB, Redis, workers) runs as a Docker Compose service.

## Key Takeaways
- `docker compose up --build` is the standard single-command deploy for ~20 projects
- Pin port to non-default for PostgreSQL (5433, not 5432) to avoid local conflicts
- Backend and frontend build separately — rebuild only what changed to save time
- Always copy `.env.example` → `.env` before first run
- `deploy.sh` script in some projects (GMAL) automates env setup + build

## When to Use
Any project with 2+ services (e.g., API + DB, or frontend + backend + DB).

## Standard Compose Patterns

### 3-Service Stack (Most Common)
```yaml
# docker-compose.yml
services:
  frontend:
    build: ./frontend
    ports: ["3000:3000"]
    depends_on: [backend]

  backend:
    build: ./backend
    ports: ["8000:8000"]
    env_file: .env
    depends_on: [db]

  db:
    image: postgres:16
    ports: ["5433:5432"]   # non-default external port
    volumes: ["pgdata:/var/lib/postgresql/data"]
    environment:
      POSTGRES_DB: mydb
      POSTGRES_USER: myuser
      POSTGRES_PASSWORD: mypass

volumes:
  pgdata:
```

### Full AI Stack (Enterprise Nexus)
```yaml
services:
  frontend:   # Next.js 14
  backend:    # FastAPI + Celery
  postgres:   # PostgreSQL 16 (nexus-postgres)
  redis:      # Redis 7 (nexus-redis)
  qdrant:     # Qdrant vector DB (nexus-qdrant)
  worker:     # Celery worker
  beat:       # Celery beat scheduler
```

### With nginx Proxy (DeckForge)
```yaml
services:
  nginx:      # :80 → routes to frontend or backend
  frontend:   # Next.js :3000 (internal)
  backend:    # FastAPI :8000 (internal)
```

## Dev Commands
```bash
# Standard workflow
cp .env.example .env         # First time only
docker compose up --build    # Build + start all

# Faster iteration
docker compose build backend && docker compose up -d backend
docker compose logs backend --tail=50

# DB operations
docker compose exec db psql -U myuser -d mydb
docker compose exec backend alembic upgrade head

# Backup DB
docker compose exec db pg_dump -U myuser -d mydb > backups/dump.sql
```

## Production Deploy (Sandbox NotebookLM Pattern)
```bash
# On server (optical-web-1)
git pull origin main
docker compose build backend   # or frontend or both
docker compose up -d
docker compose logs backend --tail=50
```

## Port Conventions
| Service | Internal Port | External Port |
|---------|--------------|---------------|
| FastAPI backend | 8000 | 8000 or 8001 |
| React/Vite frontend | 3000 | 3000 or 3010 |
| Next.js | 3000 | 3000 |
| PostgreSQL | 5432 | **5433** (avoid local conflict) |
| Redis | 6379 | 6379 |
| Qdrant | 6333 | 6333 |

## Projects Using This Pattern
- [[01 Projects/enterprise-ai-hub-nexus/Enterprise AI Hub Nexus|Enterprise Nexus]] — 7-service compose (frontend, backend, postgres, redis, qdrant, worker, beat)
- [[01 Projects/gmal-scope-builder/GMAL Scope Builder|GMAL Scope Builder]] — 3-service (frontend :3010, backend :8001, db :5433)
- [[01 Projects/modcomms/Mod Comms|Mod Comms]] — 3-service, deployed to GCP
- [[01 Projects/video-accessibility/Video Accessibility Platform|Video Accessibility]] — multi-worker compose with Celery + MongoDB
- [[01 Projects/sandbox-notebookllamalm-nextjs/Sandbox NotebookLM|Sandbox NotebookLM]] — Next.js 15 + FastAPI + PostgreSQL 18 + Redis 7

## Gotchas & Lessons
- Don't use port 5432 externally for PostgreSQL — conflicts with local Postgres installs
- `.env.example` → `.env` step is almost always required; document it in README
- `docker compose up --build` every time is safe but slow — use targeted `build backend` for iteration
- Celery workers need their own container with separate `command:` override
- Volume names should be prefixed with project name to avoid sharing data between projects

## Related
- [[wiki/tech-patterns/fastapi-python-docker|fastapi-python-docker]] — the backend being deployed
- [[wiki/tech-patterns/redis-celery-worker-queue|redis-celery-worker-queue]] — worker services
- [[wiki/architecture/gcp-deployment-lb-timeout|gcp-deployment-lb-timeout]] — GCP-specific constraints
