Docker-first development architecture

· Tech

TLDR: Run your API and database in Docker Compose. Inject secrets with infisical run -- docker compose up. Run AI agents in a separate sandbox with only source code. No .env files on disk. Secrets exist only in container memory. AI cannot see them.


Your dev stack needs secrets. Your AI agent needs your codebase. These two must never share the same environment.

Architecture

HOST MACHINE
├── Docker Compose (dev stack)
│ ├── API container (secrets via Infisical)
│ ├── PostgreSQL, Redis
│ └── Frontend (public vars only)
├── AI Sandbox (Claude Code / Codex)
│ ├── Source code (mounted)
│ └── NO secrets, NO env vars
├── Infisical CLI (fetches secrets, injects into Docker)
└── 1Password (personal passwords, SSH keys, 2FA)

docker-compose.dev.yml

services:
api:
build: { context: ., dockerfile: Dockerfile.dev }
volumes: [./apps/api/src:/app/apps/api/src]
ports: ["3333:3333"]
environment:
- DATABASE_URL # No value = inherited from host
- DATABASE_PASSWORD # Infisical populates these
- STRIPE_SECRET_KEY
- JWT_SECRET
depends_on: [postgres, redis]
web:
build: { context: ., dockerfile: Dockerfile.dev }
volumes: [./apps/web/src:/app/apps/web/src]
ports: ["3000:3000"]
environment:
- NEXT_PUBLIC_API_URL=http://localhost:3333
postgres:
image: postgres:16-alpine
environment:
{
POSTGRES_DB: myapp_dev,
POSTGRES_USER: myapp,
POSTGRES_PASSWORD: dev-password,
}
# No host ports. Only reachable by other containers on the same network.
redis:
image: redis:7-alpine
# No host ports. Only reachable by other containers on the same network.

Starting the stack

Terminal window
# Infrastructure first
docker compose -f docker-compose.dev.yml up -d postgres redis
# API with secrets injected
infisical run --env=dev -- docker compose -f docker-compose.dev.yml up -d api
# Frontend (no secrets needed)
docker compose -f docker-compose.dev.yml up -d web

Infisical fetches secrets and injects them as env vars. Docker Compose passes them to the container. Nothing touches disk. See Setting up Infisical for the setup.

Common tasks

Terminal window
# Logs
docker compose -f docker-compose.dev.yml logs -f api
# Migrations
infisical run --env=dev -- docker compose -f docker-compose.dev.yml exec api node ace migration:run
# Tests
infisical run --env=dev -- docker compose -f docker-compose.dev.yml exec api npm test
# Rebuild
infisical run --env=dev -- docker compose -f docker-compose.dev.yml up -d --build api
# Restart after secret rotation
infisical run --env=dev -- docker compose -f docker-compose.dev.yml restart api

The .env.example file

Document which env vars exist. Commit to git with placeholder values. Real .env files must never exist.

.env.example
DATABASE_URL=postgresql://localhost:5432/myapp_dev
STRIPE_SECRET_KEY=sk_test_replace_me
JWT_SECRET=replace-me