Status: ✅ COMPLETED
Date: November 22, 2025
Duration: Phase 2.1 + 2.2
Phase 2 focused on containerizing the DoIt API and building a complete local development environment using Docker and Docker Compose. This phase establishes the foundation for Kubernetes (Phase 5) and AWS deployment (Phase 6).
Build an optimized, production-ready Docker image for the Go application using multi-stage builds and best practices.
- Before: Naive build would be ~1.2GB
- After: 58MB (95% size reduction)
- Breakdown:
- Alpine base: 8MB
- Go binary: 30MB (includes Swagger, SQLC, PostgreSQL drivers, Redis, JWT)
- Runtime deps: 5MB (ca-certificates, tzdata)
- Timezone data: 1.5MB
- Cold build: ~2 minutes (first time)
- Cached build: ~30 seconds (90% faster)
- Layer caching: Optimized (go.mod in separate layer)
- ✅ Non-root user (
appuser, UID 1000) - ✅ Minimal Alpine base (smaller attack surface)
- ✅ File ownership properly configured (
--chown=appuser:appuser) - ✅ No unnecessary tools in runtime image
- ✅ Security headers implemented
- ✅ OCI standard labels (title, description, version, created)
- ✅ Custom labels (commit SHA, Go version, maintainer)
- ✅ Build information embedded in binary (via ldflags)
- ✅ Verified via
docker inspectand runtime logs
- ✅ Multi-stage build (builder + runtime)
- ✅ Healthcheck instruction (commented for flexibility)
- ✅ Optimized compilation flags (
CGO_ENABLED=0,-ldflags='-w -s',-trimpath) - ✅
.dockerignoreimplemented (excludes test files, docs, etc.)
infra/docker/dockerfile.service- Production Dockerfileinfra/docker/DOCKER_MULTISTAGE_IMPLEMENTATION.md- Complete guideinfra/docker/QUICK_REFERENCE.md- Command referenceinfra/docker/VISUAL_GUIDE.md- Architecture diagramsinfra/docker/README.md- Docker documentation index
make docker-build # Build with metadata
make docker-build-no-cache # Build without cache
make docker-run # Run container locally
make docker-inspect # View metadata
make docker-size # Show image size
make docker-shell # Get shell access
make docker-clean # Remove images- Multi-Stage Builds: Separate build and runtime environments for minimal image size
- Layer Caching: Order Dockerfile commands from least to most frequently changing
- Build Arguments: Inject metadata (version, commit, date) at build time
- Non-Root Users: Security best practice for production containers
- Alpine vs Distroless: Trade-offs between size, debugging, and compatibility
Create a complete multi-container development environment that orchestrates all services needed for local development.
| Service | Image | Purpose | Port | Status |
|---|---|---|---|---|
| API | Built from Dockerfile | Go application | 8080 | ✅ |
| PostgreSQL | postgres:16-alpine | Primary database | 5432 | ✅ |
| Redis | redis:7-alpine | Caching layer | 6379 | ✅ |
| Prometheus | prom/prometheus | Metrics collection | 9090 | ✅ |
| Grafana | grafana/grafana | Metrics visualization | 3000 | ✅ |
| Adminer | adminer | Database UI (optional) | 8081 | ✅ |
- ✅ Custom bridge network (
doit_network) - ✅ Service discovery via Docker DNS
- ✅ Services reach each other by name (e.g.,
postgres,redis) - ✅ Isolated from other Docker containers
- ✅ 4 named volumes created:
doit_postgres_data- Database datadoit_redis_data- Cache datadoit_prometheus_data- Metrics datadoit_grafana_data- Dashboard configurations
- ✅ Data survives
docker-compose down - ✅ Easy to backup and restore
- ✅ All services have health checks
- ✅ Proper startup ordering:
- PostgreSQL starts → healthy
- Redis starts → healthy
- API starts (depends on both)
- Prometheus/Grafana start
- ✅
depends_onwithcondition: service_healthy
- ✅
.env.exampletemplate - ✅ Environment variable substitution
- ✅ Defaults for development (can override)
- ✅ Sensitive values excluded from git
- ✅ Hot reload (source code mounted as volume)
- ✅ Easy log access (
make compose-logs-api) - ✅ Shell access to containers
- ✅ Database GUI (Adminer with profiles)
- ✅ Prometheus configured to scrape API
/metrics - ✅ Grafana provisioned with:
- Prometheus datasource (auto-configured)
- Sample dashboard (API overview)
- ✅ 30-day metrics retention
docker-compose.yml- Service orchestration.env.example- Environment templateinfra/docker/prometheus.yml- Prometheus configinfra/docker/grafana/provisioning/datasources/prometheus.yml- Auto datasourceinfra/docker/grafana/provisioning/dashboards/default.yml- Dashboard loaderinfra/docker/grafana/dashboards/api-overview.json- Sample dashboardinfra/docker/DOCKER_COMPOSE_MENTAL_MODEL.md- Conceptual guideinfra/docker/DOCKER_COMPOSE_IMPLEMENTATION.md- Implementation guideinfra/docker/DOCKER_COMPOSE_QUICK_REFERENCE.md- Command cheat sheet
make compose-up # Start all services
make compose-up-build # Build and start
make compose-down # Stop services
make compose-down-v # Stop and remove volumesmake compose-logs # All logs
make compose-logs-api # API logs only
make compose-logs-db # PostgreSQL logs
make compose-ps # List services
make compose-health # Health check allmake compose-restart # Restart all
make compose-restart-api # Restart API only
make compose-shell-api # Shell in API container
make compose-shell-db # psql in PostgreSQL
make compose-shell-redis # redis-climake compose-migrate-up # Run migrations
make compose-migrate-down # Rollback migrationsmake compose-setup # Create .env file
make compose-pull # Update images
make compose-stats # Resource usage
make compose-up-tools # Start with Adminer- Service Discovery: Container names resolve via DNS in custom networks
- Health Checks: Critical for proper startup ordering with
depends_on - Volume Types: Named volumes for data, bind mounts for development
- Environment Variables:
.envfile + defaults indocker-compose.yml - Restart Policies:
unless-stoppedfor resilience - Profiles: Optional services (Adminer) with
--profile tools - Provisioning: Auto-configure Grafana datasources and dashboards
- ✅ Multi-stage builds
- ✅ Layer caching optimization
- ✅ Image size optimization
- ✅ Dockerfile best practices
- ✅ Non-root users
- ✅ Build arguments and labels
- ✅
.dockerignoreusage
- ✅ Service orchestration
- ✅ Networking (bridge, DNS)
- ✅ Volume management (named, bind)
- ✅ Environment variable strategies
- ✅ Health checks and dependencies
- ✅ Service profiles
- ✅ Port mapping
- ✅ Infrastructure as Code (docker-compose.yml)
- ✅ Configuration management (.env files)
- ✅ Automated setup (Makefile targets)
- ✅ Monitoring setup (Prometheus + Grafana)
- ✅ Documentation (comprehensive guides)
- ✅ Microservices communication
- ✅ Service discovery
- ✅ Data persistence strategies
- ✅ Observability setup
- ✅ Development vs production patterns
- Image size: 58MB (target: <100MB) ✅
- Build time (cached): 30 seconds ✅
- Layer optimization: Achieved ✅
- Security score: Non-root, minimal base ✅
- Services: 6 (API, PostgreSQL, Redis, Prometheus, Grafana, Adminer)
- Networks: 1 custom bridge
- Volumes: 4 named volumes
- Health checks: 5 services
- Makefile targets: 20+ commands
- Documentation: 3 comprehensive guides
- Setup time: < 2 minutes (first time)
- Start time: < 30 seconds (after images pulled)
- Command to start: 1 (
make compose-up) - Services running: 6 containers
- Log access: Instant (
make compose-logs-api) - Data persistence: Across restarts ✅
-
Phase 3.1: Structured Logging
- Request ID propagation
- Contextual logging
- JSON logs for production
-
Phase 3.2: Metrics Implementation
- Instrument HTTP handlers
- Database query metrics
- Custom business metrics
- Enhance Grafana dashboards
-
Phase 3.3: Distributed Tracing
- OpenTelemetry integration
- Jaeger setup
- Trace context propagation
- Phase 4: Architecture Patterns & Caching
- Phase 5: Kubernetes & Helm (builds on Docker Compose knowledge)
- Phase 6: AWS Deployment (ECS Fargate, EKS)
- Phase 7: Advanced DevOps & CI/CD
- Phase 8: Production Operations & Scale
- Main README - Updated with Phase 2 completion
- Kubernetes Roadmap
- Learning Methodology Prompt
All objectives achieved:
- Build optimized Docker image (<100MB)
- Implement multi-stage build
- Add build metadata and traceability
- Create complete docker-compose stack
- Set up networking and volumes
- Implement health checks
- Configure monitoring (Prometheus + Grafana)
- Create comprehensive documentation
- Add convenient Makefile targets
- Test entire stack locally
Understanding Docker's layering, caching, and networking concepts before implementation made the process smooth and intuitive.
Writing comprehensive guides solidified understanding and provides reference for future team members.
Following industry best practices (multi-stage builds, non-root users, health checks) from the start prevents technical debt.
Building a local environment that mirrors production patterns (service discovery, health checks, monitoring) reduces surprises during deployment.
Makefile targets and docker-compose make complex operations simple and repeatable.
Phase 2 successfully established a production-grade local development environment with:
- ✅ Optimized, secure Docker images
- ✅ Complete multi-container orchestration
- ✅ Monitoring and observability foundations
- ✅ Excellent developer experience
- ✅ Comprehensive documentation
The foundation is now rock-solid for Phase 3 (Observability) and beyond!
Ready for Phase 3? Let's add structured logging, metrics instrumentation, and distributed tracing! 🚀