User: droneflow needs a staging environment that mirrors production. Right now we only have a docker-compose.yml for local dev. I want a separate staging compose file with Traefik as the router and real Postgres instead of the local volume.

Agent: [Read: docker-compose.yml]

```yaml
services:
  api:
    build: .
    ports:
      - "3000:3000"
    environment:
      DATABASE_URL: postgres://dev:dev@db:5432/droneflow
  db:
    image: postgres:16
    volumes:
      - pgdata:/var/lib/postgresql/data
```

For staging I'll create docker-compose.staging.yml that:
- Removes the direct port binding (Traefik will handle routing)
- Adds Traefik as a router service with Let's Encrypt TLS
- Points DATABASE_URL at an external Postgres instance (staging.db.example.com)
- Adds explicit labels so Traefik routes api.staging.example.com to the api service

User: The staging database is already provisioned at staging.db.example.com. Use that.

Agent: [Write: docker-compose.staging.yml]

```yaml
services:
  traefik:
    image: traefik:v3.1
    command:
      - --providers.docker=true
      - --providers.docker.exposedbydefault=false
      - --entrypoints.web.address=:80
      - --entrypoints.websecure.address=:443
      - --certificatesresolvers.letsencrypt.acme.email=ops@example.com
      - --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json
      - --certificatesresolvers.letsencrypt.acme.tlschallenge=true
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - letsencrypt:/letsencrypt

  api:
    build: .
    environment:
      DATABASE_URL: postgres://app:${STAGING_DB_PASSWORD}@staging.db.example.com:5432/droneflow_staging
      NODE_ENV: staging
    labels:
      - traefik.enable=true
      - traefik.http.routers.api.rule=Host(`api.staging.example.com`)
      - traefik.http.routers.api.entrypoints=websecure
      - traefik.http.routers.api.tls.certresolver=letsencrypt

volumes:
  letsencrypt:
```

User: Should the staging environment auto-pull new images on deploy?

Agent: Not with compose alone. For that you'd need Watchtower or a deploy hook. The typical pattern is to keep deploy explicit: SSH to the staging host and run `docker compose -f docker-compose.staging.yml pull && docker compose -f docker-compose.staging.yml up -d`. That way staging only updates when you intentionally trigger a deploy, not on every push.

User: Let's keep it explicit for now. Add a deploy script.

Agent: [Write: scripts/deploy-staging.sh]

```bash
#!/usr/bin/env bash
set -euo pipefail
HOST="${STAGING_HOST:-staging.example.com}"
ssh "deploy@${HOST}" '
  cd /opt/droneflow
  docker compose -f docker-compose.staging.yml pull
  docker compose -f docker-compose.staging.yml up -d --remove-orphans
'
```

User: Good. Any concerns about the current setup?

Agent: One: the `STAGING_DB_PASSWORD` must be set in the environment on the staging host before starting the stack. If it's missing, Docker Compose substitutes an empty string and Postgres connection will fail at startup rather than at config validation time. Consider adding a `.env.staging.example` file documenting required variables.

User: Good call. Add that.

Agent: [Write: .env.staging.example]

```
STAGING_DB_PASSWORD=changeme
```

Setup complete. Traefik routes api.staging.example.com with TLS; deploy is explicit via the deploy script.
