DEV Community

Cover image for How I Reduced Docker Setup Time from 8 Hours to 5 Minutes
Penuel Mdluli
Penuel Mdluli

Posted on

How I Reduced Docker Setup Time from 8 Hours to 5 Minutes

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"]
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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 --
Enter fullscreen mode Exit fullscreen mode

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'
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Step 2: Configure environment

cp .env.example .env
# Edit .env with your passwords and settings
Enter fullscreen mode Exit fullscreen mode

Step 3: Launch!

docker-compose up -d
Enter fullscreen mode Exit fullscreen mode

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:

👉 DevOps Docker Starter Kits

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:

  1. Identify repetitive tasks in your workflow
  2. Create a template for your most common stack
  3. Document it thoroughly
  4. Share it with your team
  5. 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
Enter fullscreen mode Exit fullscreen mode

Top comments (0)