<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Penuel Mdluli</title>
    <description>The latest articles on DEV Community by Penuel Mdluli (@penuel_mdluli_bc27cc2c8fe).</description>
    <link>https://dev.to/penuel_mdluli_bc27cc2c8fe</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3701143%2F125bd599-870c-4f27-9b86-feeb801bd522.jpg</url>
      <title>DEV Community: Penuel Mdluli</title>
      <link>https://dev.to/penuel_mdluli_bc27cc2c8fe</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/penuel_mdluli_bc27cc2c8fe"/>
    <language>en</language>
    <item>
      <title>How I Reduced Docker Setup Time from 8 Hours to 5 Minutes</title>
      <dc:creator>Penuel Mdluli</dc:creator>
      <pubDate>Thu, 08 Jan 2026 19:11:41 +0000</pubDate>
      <link>https://dev.to/penuel_mdluli_bc27cc2c8fe/how-i-reduced-docker-setup-time-from-8-hours-to-5-minutes-133p</link>
      <guid>https://dev.to/penuel_mdluli_bc27cc2c8fe/how-i-reduced-docker-setup-time-from-8-hours-to-5-minutes-133p</guid>
      <description>&lt;h2&gt;
  
  
  The Problem Every Developer Faces
&lt;/h2&gt;

&lt;p&gt;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:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⏰ 2-3 hours writing optimized Dockerfiles&lt;/li&gt;
&lt;li&gt;⏰ 2-3 hours configuring docker-compose for multiple environments
&lt;/li&gt;
&lt;li&gt;⏰ 1-2 hours setting up health checks&lt;/li&gt;
&lt;li&gt;⏰ 1-2 hours implementing security best practices&lt;/li&gt;
&lt;li&gt;⏰ 2-3 hours creating CI/CD pipelines&lt;/li&gt;
&lt;li&gt;⏰ 1-2 hours writing documentation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Total: 8-16 hours of repetitive work before writing a single line of business logic.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This was slowing down our ability to ship features. So I did something about it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: Production-Ready Templates
&lt;/h2&gt;

&lt;p&gt;I created reusable Docker templates for the most common web application stacks. Here's what a typical setup looks like:&lt;/p&gt;

&lt;h3&gt;
  
  
  The Multi-Stage Dockerfile
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# ==========================================&lt;/span&gt;
&lt;span class="c"&gt;# .NET Core 8 API Production Dockerfile&lt;/span&gt;
&lt;span class="c"&gt;# Multi-stage build for optimized image size&lt;/span&gt;
&lt;span class="c"&gt;# ==========================================&lt;/span&gt;

&lt;span class="c"&gt;# Stage 1: Build&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;mcr.microsoft.com/dotnet/sdk:8.0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;build&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /src&lt;/span&gt;

&lt;span class="c"&gt;# Copy csproj and restore dependencies (cached layer)&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; ["YourApiName.csproj", "./"]&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;dotnet restore &lt;span class="s2"&gt;"YourApiName.csproj"&lt;/span&gt;

&lt;span class="c"&gt;# Copy source code and build&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;dotnet build &lt;span class="s2"&gt;"YourApiName.csproj"&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; Release &lt;span class="nt"&gt;-o&lt;/span&gt; /app/build

&lt;span class="c"&gt;# Stage 2: Publish&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;build&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;publish&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;dotnet publish &lt;span class="s2"&gt;"YourApiName.csproj"&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; Release &lt;span class="nt"&gt;-o&lt;/span&gt; /app/publish

&lt;span class="c"&gt;# Stage 3: Runtime&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;mcr.microsoft.com/dotnet/aspnet:8.0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;final&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="c"&gt;# Create non-root user for security&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;addgroup &lt;span class="nt"&gt;--system&lt;/span&gt; &lt;span class="nt"&gt;--gid&lt;/span&gt; 1000 appuser &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    adduser &lt;span class="nt"&gt;--system&lt;/span&gt; &lt;span class="nt"&gt;--uid&lt;/span&gt; 1000 &lt;span class="nt"&gt;--ingroup&lt;/span&gt; appuser appuser

&lt;span class="c"&gt;# Copy published files&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=publish /app/publish .&lt;/span&gt;

&lt;span class="c"&gt;# Set ownership&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; appuser:appuser /app

&lt;span class="c"&gt;# Switch to non-root user&lt;/span&gt;
&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; appuser&lt;/span&gt;

&lt;span class="c"&gt;# Expose port&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 8080&lt;/span&gt;

&lt;span class="c"&gt;# Health check&lt;/span&gt;
&lt;span class="k"&gt;HEALTHCHECK&lt;/span&gt;&lt;span class="s"&gt; --interval=30s --timeout=3s --start-period=40s --retries=3 \&lt;/span&gt;
  CMD curl -f http://localhost:8080/health || exit 1

&lt;span class="c"&gt;# Entry point&lt;/span&gt;
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["dotnet", "YourApiName.dll"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why multi-stage?&lt;/strong&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build image: ~2GB (includes SDK)&lt;/li&gt;
&lt;li&gt;Runtime image: ~200MB (only runtime needed)&lt;/li&gt;
&lt;li&gt;90% size reduction!&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The docker-compose.yml
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.8'&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# PostgreSQL Database&lt;/span&gt;
  &lt;span class="na"&gt;postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:16-alpine&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api-postgres&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_DB&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${DB_NAME:-apidb}&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${DB_USER:-apiuser}&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${DB_PASSWORD:-SecurePassword123!}&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${DB_PORT:-5432}:5432"&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;postgres_data:/var/lib/postgresql/data&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;app-network&lt;/span&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CMD-SHELL"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pg_isready&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-U&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;${DB_USER:-apiuser}"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10s&lt;/span&gt;
      &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;
      &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;

  &lt;span class="c1"&gt;# .NET API Application&lt;/span&gt;
  &lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
      &lt;span class="na"&gt;dockerfile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Dockerfile&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dotnet-api&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ASPNETCORE_ENVIRONMENT=${ENVIRONMENT:-Production}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ConnectionStrings__DefaultConnection=Host=postgres;Port=5432;...&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${API_PORT:-5000}:8080"&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;service_healthy&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;app-network&lt;/span&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CMD"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;curl"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-f"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:8080/health"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;30s&lt;/span&gt;

  &lt;span class="c1"&gt;# Redis Cache&lt;/span&gt;
  &lt;span class="na"&gt;redis&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis:7-alpine&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis-server --requirepass ${REDIS_PASSWORD}&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;redis_data:/data&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;app-network&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;postgres_data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;redis_data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;app-network&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bridge&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Key Features That Make It Production-Ready
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. 🔒 Security Hardening
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Non-root user execution&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;adduser &lt;span class="nt"&gt;--system&lt;/span&gt; &lt;span class="nt"&gt;--uid&lt;/span&gt; 1000 appuser
&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; appuser&lt;/span&gt;

&lt;span class="c"&gt;# No hardcoded secrets - all in .env&lt;/span&gt;
POSTGRES_PASSWORD: ${DB_PASSWORD}

# Minimal base images (Alpine Linux)
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; postgres:16-alpine&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. 🏥 Comprehensive Health Checks
&lt;/h3&gt;

&lt;p&gt;Every service has proper health checks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CMD-SHELL"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pg_isready&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-U&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;${DB_USER}"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10s&lt;/span&gt;
  &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;
  &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
  &lt;span class="na"&gt;start_period&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;40s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Services start in correct order&lt;/li&gt;
&lt;li&gt;Orchestration tools know service status&lt;/li&gt;
&lt;li&gt;Automatic restarts on failures&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. 🌍 Environment-Specific Configs
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Development&lt;/span&gt;
docker-compose up &lt;span class="nt"&gt;-d&lt;/span&gt;

&lt;span class="c"&gt;# Production&lt;/span&gt;
docker-compose &lt;span class="nt"&gt;-f&lt;/span&gt; docker-compose.prod.yml up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Different settings for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Logging levels&lt;/li&gt;
&lt;li&gt;Debug modes&lt;/li&gt;
&lt;li&gt;Resource limits&lt;/li&gt;
&lt;li&gt;Service dependencies&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. 📦 Automated Backups
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# Automated PostgreSQL backup script&lt;/span&gt;

&lt;span class="nv"&gt;TIMESTAMP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +&lt;span class="s2"&gt;"%Y%m%d_%H%M%S"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;BACKUP_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"backup_&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TIMESTAMP&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.sql"&lt;/span&gt;

docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; postgres pg_dump &lt;span class="nt"&gt;-U&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DB_USER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DB_NAME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$BACKUP_FILE&lt;/span&gt;
&lt;span class="nb"&gt;gzip&lt;/span&gt; &lt;span class="nv"&gt;$BACKUP_FILE&lt;/span&gt;

&lt;span class="c"&gt;# Keep only last 7 backups&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; backup_&lt;span class="k"&gt;*&lt;/span&gt;.sql.gz | &lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; +8 | xargs &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. 🚀 CI/CD Integration
&lt;/h3&gt;

&lt;p&gt;Jenkins pipeline included:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;pipeline&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="n"&gt;any&lt;/span&gt;
    &lt;span class="n"&gt;stages&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Build'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'docker-compose build'&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Test'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'docker-compose run api dotnet test'&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Deploy'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'docker-compose -f docker-compose.prod.yml up -d'&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Results
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Before Templates:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;⏱️ 8-16 hours per project&lt;/li&gt;
&lt;li&gt;😓 Repetitive, error-prone work&lt;/li&gt;
&lt;li&gt;📚 Inconsistent configurations&lt;/li&gt;
&lt;li&gt;🐛 Security issues discovered late&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  After Templates:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;⚡ 5 minutes per project&lt;/li&gt;
&lt;li&gt;✅ Consistent, tested configurations&lt;/li&gt;
&lt;li&gt;🔒 Security baked in from day one&lt;/li&gt;
&lt;li&gt;📖 Documentation included&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;I created templates for the 4 most common stacks I work with:&lt;/p&gt;

&lt;h3&gt;
  
  
  Stack 1: .NET Core + PostgreSQL + Redis
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;ASP.NET Core 8 Web API&lt;/li&gt;
&lt;li&gt;PostgreSQL 16 database&lt;/li&gt;
&lt;li&gt;Redis caching&lt;/li&gt;
&lt;li&gt;PgAdmin UI (dev mode)&lt;/li&gt;
&lt;li&gt;Complete CRUD examples&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Stack 2: Node.js + MongoDB + Redis
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Express.js API with Node 20&lt;/li&gt;
&lt;li&gt;MongoDB 7 + auto-initialization&lt;/li&gt;
&lt;li&gt;Redis caching with examples&lt;/li&gt;
&lt;li&gt;Mongo Express UI (dev mode)&lt;/li&gt;
&lt;li&gt;Sample API with caching patterns&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Stack 3: Python Django + PostgreSQL
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Django 5 + REST Framework&lt;/li&gt;
&lt;li&gt;PostgreSQL 16&lt;/li&gt;
&lt;li&gt;Gunicorn production server&lt;/li&gt;
&lt;li&gt;WhiteNoise for static files&lt;/li&gt;
&lt;li&gt;Complete REST API example&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Stack 4: Laravel + MySQL + Redis
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Laravel 11 + PHP 8.3 FPM&lt;/li&gt;
&lt;li&gt;Nginx web server&lt;/li&gt;
&lt;li&gt;MySQL 8.0 database&lt;/li&gt;
&lt;li&gt;Redis for sessions/cache&lt;/li&gt;
&lt;li&gt;Supervisor process management&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each template includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Production-ready Dockerfiles&lt;/li&gt;
&lt;li&gt;✅ docker-compose.yml (dev + prod)&lt;/li&gt;
&lt;li&gt;✅ Automated backup scripts&lt;/li&gt;
&lt;li&gt;✅ Health checks for all services&lt;/li&gt;
&lt;li&gt;✅ CI/CD pipelines (Jenkins, GitHub Actions)&lt;/li&gt;
&lt;li&gt;✅ Security hardening&lt;/li&gt;
&lt;li&gt;✅ Complete documentation&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to Use These Templates
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Copy the template for your stack
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone your-template
&lt;span class="nb"&gt;cd &lt;/span&gt;your-template
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Configure environment
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env
&lt;span class="c"&gt;# Edit .env with your passwords and settings&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Launch!
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. Your production-ready infrastructure is running.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Invest in Templates Early
&lt;/h3&gt;

&lt;p&gt;Creating these templates took about 40 hours initially. But I've now saved 8+ hours on each of 20+ projects. &lt;strong&gt;Total time saved: 160+ hours.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Security Can't Be an Afterthought
&lt;/h3&gt;

&lt;p&gt;Building security into templates means every project starts secure by default:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Non-root users&lt;/li&gt;
&lt;li&gt;No hardcoded secrets&lt;/li&gt;
&lt;li&gt;Proper networking&lt;/li&gt;
&lt;li&gt;Health monitoring&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Documentation is Part of the Product
&lt;/h3&gt;

&lt;p&gt;Each template includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Quick start guide&lt;/li&gt;
&lt;li&gt;Configuration reference&lt;/li&gt;
&lt;li&gt;Troubleshooting section&lt;/li&gt;
&lt;li&gt;Best practices&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Good docs mean faster onboarding for new team members.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. CI/CD Should Be Included
&lt;/h3&gt;

&lt;p&gt;Don't make developers set up pipelines separately. Include working Jenkins and GitHub Actions configs in the template.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Templates Are Available
&lt;/h2&gt;

&lt;p&gt;I've packaged all four templates into a bundle that you can use for your projects:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://528587523696.gumroad.com/l/prxbh" rel="noopener noreferrer"&gt;DevOps Docker Starter Kits&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each template saves 4-8 hours of setup time, so across all four stacks, you're saving 16-32 hours of repetitive DevOps work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try This Approach Yourself
&lt;/h2&gt;

&lt;p&gt;Even if you don't use my templates, I highly recommend creating your own:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Identify repetitive tasks&lt;/strong&gt; in your workflow&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create a template&lt;/strong&gt; for your most common stack&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Document it thoroughly&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Share it with your team&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Iterate based on feedback&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The time investment upfront pays massive dividends over time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Docker is powerful, but the initial setup is time-consuming and error-prone. By creating reusable, production-ready templates, I've:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⚡ Reduced setup time by 95%&lt;/li&gt;
&lt;li&gt;✅ Improved consistency across projects&lt;/li&gt;
&lt;li&gt;🔒 Made security the default&lt;/li&gt;
&lt;li&gt;📚 Created better documentation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What repetitive tasks could you template in your workflow?&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;About the Author:&lt;/strong&gt; I'm a DevOps Engineer managing mobile banking infrastructure across 9 African markets. I specialize in Docker, Kubernetes, and CI/CD automation.&lt;/p&gt;

&lt;p&gt;Have questions about the templates or Docker setup? Drop them in the comments! 👇&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


---


#DevOps #Docker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>docker</category>
      <category>devops</category>
      <category>webdev</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
