The Problem Every Developer Faces
As a DevOps Engineer managing infrastructure across 9 African markets at ABSA Bank, I've set up Docker environments for countless projects. Each time, it's the same routine:
- ⏰ 2-3 hours writing optimized Dockerfiles
- ⏰ 2-3 hours configuring docker-compose for multiple environments
- ⏰ 1-2 hours setting up health checks
- ⏰ 1-2 hours implementing security best practices
- ⏰ 2-3 hours creating CI/CD pipelines
- ⏰ 1-2 hours writing documentation
Total: 8-16 hours of repetitive work before writing a single line of business logic.
This was slowing down our ability to ship features. So I did something about it.
The Solution: Production-Ready Templates
I created reusable Docker templates for the most common web application stacks. Here's what a typical setup looks like:
The Multi-Stage Dockerfile
# ==========================================
# .NET Core 8 API Production Dockerfile
# Multi-stage build for optimized image size
# ==========================================
# Stage 1: Build
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
# Copy csproj and restore dependencies (cached layer)
COPY ["YourApiName.csproj", "./"]
RUN dotnet restore "YourApiName.csproj"
# Copy source code and build
COPY . .
RUN dotnet build "YourApiName.csproj" -c Release -o /app/build
# Stage 2: Publish
FROM build AS publish
RUN dotnet publish "YourApiName.csproj" -c Release -o /app/publish
# Stage 3: Runtime
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS final
WORKDIR /app
# Create non-root user for security
RUN addgroup --system --gid 1000 appuser && \
adduser --system --uid 1000 --ingroup appuser appuser
# Copy published files
COPY --from=publish /app/publish .
# Set ownership
RUN chown -R appuser:appuser /app
# Switch to non-root user
USER appuser
# Expose port
EXPOSE 8080
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s --retries=3 \
CMD curl -f http://localhost:8080/health || exit 1
# Entry point
ENTRYPOINT ["dotnet", "YourApiName.dll"]
Why multi-stage?
- Build image: ~2GB (includes SDK)
- Runtime image: ~200MB (only runtime needed)
- 90% size reduction!
The docker-compose.yml
version: '3.8'
services:
# PostgreSQL Database
postgres:
image: postgres:16-alpine
container_name: api-postgres
restart: unless-stopped
environment:
POSTGRES_DB: ${DB_NAME:-apidb}
POSTGRES_USER: ${DB_USER:-apiuser}
POSTGRES_PASSWORD: ${DB_PASSWORD:-SecurePassword123!}
ports:
- "${DB_PORT:-5432}:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- app-network
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-apiuser}"]
interval: 10s
timeout: 5s
retries: 5
# .NET API Application
api:
build:
context: .
dockerfile: Dockerfile
container_name: dotnet-api
restart: unless-stopped
environment:
- ASPNETCORE_ENVIRONMENT=${ENVIRONMENT:-Production}
- ConnectionStrings__DefaultConnection=Host=postgres;Port=5432;...
ports:
- "${API_PORT:-5000}:8080"
depends_on:
postgres:
condition: service_healthy
networks:
- app-network
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
# Redis Cache
redis:
image: redis:7-alpine
restart: unless-stopped
command: redis-server --requirepass ${REDIS_PASSWORD}
volumes:
- redis_data:/data
networks:
- app-network
volumes:
postgres_data:
redis_data:
networks:
app-network:
driver: bridge
Key Features That Make It Production-Ready
1. 🔒 Security Hardening
# Non-root user execution
RUN adduser --system --uid 1000 appuser
USER appuser
# No hardcoded secrets - all in .env
POSTGRES_PASSWORD: ${DB_PASSWORD}
# Minimal base images (Alpine Linux)
FROM postgres:16-alpine
2. 🏥 Comprehensive Health Checks
Every service has proper health checks:
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_USER}"]
interval: 10s
timeout: 5s
retries: 5
start_period: 40s
This ensures:
- Services start in correct order
- Orchestration tools know service status
- Automatic restarts on failures
3. 🌍 Environment-Specific Configs
# Development
docker-compose up -d
# Production
docker-compose -f docker-compose.prod.yml up -d
Different settings for:
- Logging levels
- Debug modes
- Resource limits
- Service dependencies
4. 📦 Automated Backups
#!/bin/bash
# Automated PostgreSQL backup script
TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
BACKUP_FILE="backup_${TIMESTAMP}.sql"
docker exec -t postgres pg_dump -U ${DB_USER} -d ${DB_NAME} > $BACKUP_FILE
gzip $BACKUP_FILE
# Keep only last 7 backups
ls -t backup_*.sql.gz | tail -n +8 | xargs rm --
5. 🚀 CI/CD Integration
Jenkins pipeline included:
pipeline {
agent any
stages {
stage('Build') {
steps {
sh 'docker-compose build'
}
}
stage('Test') {
steps {
sh 'docker-compose run api dotnet test'
}
}
stage('Deploy') {
steps {
sh 'docker-compose -f docker-compose.prod.yml up -d'
}
}
}
}
The Results
Before Templates:
- ⏱️ 8-16 hours per project
- 😓 Repetitive, error-prone work
- 📚 Inconsistent configurations
- 🐛 Security issues discovered late
After Templates:
- ⚡ 5 minutes per project
- ✅ Consistent, tested configurations
- 🔒 Security baked in from day one
- 📖 Documentation included
What I Built
I created templates for the 4 most common stacks I work with:
Stack 1: .NET Core + PostgreSQL + Redis
- ASP.NET Core 8 Web API
- PostgreSQL 16 database
- Redis caching
- PgAdmin UI (dev mode)
- Complete CRUD examples
Stack 2: Node.js + MongoDB + Redis
- Express.js API with Node 20
- MongoDB 7 + auto-initialization
- Redis caching with examples
- Mongo Express UI (dev mode)
- Sample API with caching patterns
Stack 3: Python Django + PostgreSQL
- Django 5 + REST Framework
- PostgreSQL 16
- Gunicorn production server
- WhiteNoise for static files
- Complete REST API example
Stack 4: Laravel + MySQL + Redis
- Laravel 11 + PHP 8.3 FPM
- Nginx web server
- MySQL 8.0 database
- Redis for sessions/cache
- Supervisor process management
Each template includes:
- ✅ Production-ready Dockerfiles
- ✅ docker-compose.yml (dev + prod)
- ✅ Automated backup scripts
- ✅ Health checks for all services
- ✅ CI/CD pipelines (Jenkins, GitHub Actions)
- ✅ Security hardening
- ✅ Complete documentation
How to Use These Templates
Step 1: Copy the template for your stack
git clone your-template
cd your-template
Step 2: Configure environment
cp .env.example .env
# Edit .env with your passwords and settings
Step 3: Launch!
docker-compose up -d
That's it. Your production-ready infrastructure is running.
Lessons Learned
1. Invest in Templates Early
Creating these templates took about 40 hours initially. But I've now saved 8+ hours on each of 20+ projects. Total time saved: 160+ hours.
2. Security Can't Be an Afterthought
Building security into templates means every project starts secure by default:
- Non-root users
- No hardcoded secrets
- Proper networking
- Health monitoring
3. Documentation is Part of the Product
Each template includes:
- Quick start guide
- Configuration reference
- Troubleshooting section
- Best practices
Good docs mean faster onboarding for new team members.
4. CI/CD Should Be Included
Don't make developers set up pipelines separately. Include working Jenkins and GitHub Actions configs in the template.
The Templates Are Available
I've packaged all four templates into a bundle that you can use for your projects:
Each template saves 4-8 hours of setup time, so across all four stacks, you're saving 16-32 hours of repetitive DevOps work.
Try This Approach Yourself
Even if you don't use my templates, I highly recommend creating your own:
- Identify repetitive tasks in your workflow
- Create a template for your most common stack
- Document it thoroughly
- Share it with your team
- Iterate based on feedback
The time investment upfront pays massive dividends over time.
Conclusion
Docker is powerful, but the initial setup is time-consuming and error-prone. By creating reusable, production-ready templates, I've:
- ⚡ Reduced setup time by 95%
- ✅ Improved consistency across projects
- 🔒 Made security the default
- 📚 Created better documentation
What repetitive tasks could you template in your workflow?
About the Author: I'm a DevOps Engineer managing mobile banking infrastructure across 9 African markets. I specialize in Docker, Kubernetes, and CI/CD automation.
Have questions about the templates or Docker setup? Drop them in the comments! 👇
---
#DevOps #Docker
Top comments (0)