malahov.io

Containerized Development Environments That Actually Work

Why we run PostgreSQL, MinIO, and the dev server inside a single Docker container — and how it simplifies onboarding and CI.

2 min read

By Alex Malahov

Every team has a setup guide. It usually starts with "install Homebrew" and ends with someone debugging a Postgres version mismatch two hours later.

One Container to Rule Them All

Our approach: a single Docker container that includes everything the app needs to run:

  • PostgreSQL 16
  • MinIO (S3-compatible object storage)
  • Bun runtime
  • Playwright for E2E testing
  • Doppler CLI for secrets
bash
bun run up    # start everything
bun run down  # stop everything

No host dependencies beyond Docker. No version conflicts. No "works on my machine."

The Worktree Trick

Each git worktree gets its own container with its own port assignment. You can run multiple feature branches simultaneously without conflicts. The container name, volume, and port are all derived from the worktree path.

Trade-offs

The image build takes 10-15 minutes cold. But you only do it once. After that, bun run up starts in seconds. The productivity gained from zero-friction onboarding far outweighs the initial build time.

When to Use This Pattern

This works well for small-to-medium teams where everyone runs the full stack locally. For larger teams with microservices, you might want a more granular approach. But for a monorepo SaaS — it's hard to beat.