# Docker Compose (Local Development)

# Docker Compose — Local Development {#docker-compose--local-development}



## Quick Start {#quick-start}


```bash
# Start all services
docker compose -f dev.compose.yaml up -d

# View logs
docker compose -f dev.compose.yaml logs -f

# Rebuild a specific service
docker compose -f dev.compose.yaml build backend

# Stop all services
docker compose -f dev.compose.yaml down
```


## dev.compose.yaml {#devcomposeyaml}


The `dev.compose.yaml` file at the project root defines all services for local development:

```yaml
services:
  postgres:
    image: postgres:15
    ports: ["5433:5432"]
    environment:
      POSTGRES_USER: appuser
      POSTGRES_PASSWORD: appsecret
      POSTGRES_DB: appdb

  redis:
    image: redis:latest
    ports: ["6372:6379"]

  backend:
    build: ./backend
    ports: ["8000:8000"]
    volumes:
      - ./backend:/api
      - ./common:/opt/common
    depends_on: [postgres, redis]

  survey-service:
    build: ./survey-service
    ports: ["8001:8000"]
    volumes:
      - ./survey-service:/api
      - ./common:/opt/common
    depends_on: [postgres, redis]
```


## Environment Variables {#environment-variables}


Shared variables go in the project root `.env`:

```bash
ENVIRONMENT=development
HOST_MAIN_DOMAIN=mquad.local
NOTIFICATION_SERVICE_TOKEN=dev-token
S3_ENDPOINT_URL=http://minio:9000
S3_ACCESS_KEY=mquadadmin
S3_SECRET_KEY=mquadadmin123
S3_BUCKET_NAME=mquad-media
```

Service-specific variables go in `<service>/.env`.


## Port Mappings (Local) {#port-mappings-local}


| Host Port | Container Port | Service |
|-----------|---------------|---------|
| 5433 | 5432 | PostgreSQL |
| 6372 | 6379 | Redis |
| 8000 | 8000 | Backend |
| 1023 | 1025 | Fake SMTP (SMTP) |
| 1083 | 8025 | Fake SMTP (Web UI) |


## Common Gotchas {#common-gotchas}



### 1. sys.path.insert for common/ module {#1-syspathinsert-for-common-module}


Every service's `api/__init__.py` must include this **before** any `from common.*` imports:

```python
import sys
sys.path.insert(0, "/opt/common")
```

Forgetting this causes `ModuleNotFoundError: No module named 'common'`.


### 2. Celery Queue Configuration {#2-celery-queue-configuration}


Each Celery worker must listen to its specific queue. Without queue separation, tasks may be picked up by the wrong worker:

```yaml
backend-worker:
  command: celery -A api.infrastructure.messaging.celery_worker worker -Q backend_celery

notification-worker:
  command: celery -A api.celery worker -Q notification
```


### 3. Redis Port {#3-redis-port}


Redis is exposed on host port **6372** (not 6379) to avoid conflicts with local Redis instances.


### 4. Alembic Migrations {#4-alembic-migrations}


Before using the backend service for the first time:

```bash
cd backend && uv run alembic upgrade head
```

For services using `create_all` (analytics-service), tables are created automatically on startup.
