Skip to main content

Command Palette

Search for a command to run...

Day 36: Docker Container Deployment - Running Nginx with Alpine 🐳

Published
17 min read
Day 36: Docker Container Deployment - Running Nginx with Alpine 🐳

Welcome back! 👋 Day 36 of the 100 Days Cloud DevOps Challenge, and today we're deploying Docker containers in production! This is fundamental container operations - creating, running, and managing containerized applications. Let's containerize! 🎯

🎯 The Mission - Deploy Nginx Container on Application Server

It's deployment day, and the DevOps team needs a containerized web server:

📋 TASK TICKET #DEV-8036 - Nginx Container Deployment
Priority: HIGH
Type: Docker Container Operations

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
PROJECT: Application Server Infrastructure
Team: Nautilus DevOps
Server: Application Server 3
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

BACKGROUND:
└─ Testing application deployment workflows
└─ Need nginx web server containerized
└─ Using lightweight Alpine Linux base
└─ Target: Application Server 3

REQUIREMENTS:

1. Container Specifications:
   └─ Name: nginx_3
   └─ Image: nginx:alpine
   └─ Server: Application Server 3

2. Container State:
   └─ Status: Running
   └─ Health: Active
   └─ Accessible: Yes

3. Verification:
   └─ Container exists
   └─ Container running
   └─ Nginx responding

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
STATUS: READY TO DEPLOY
DEADLINE: Today
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

This is containerization in action! From bare metal to isolated, portable applications! 🚀

🤔 Why Docker Containers Matter - The Container Revolution

The Traditional Deployment Problem

Before containers:

Scenario: Deploy application to 3 servers

Server 1: Ubuntu 18.04, Python 3.6
Server 2: CentOS 7, Python 3.7
Server 3: Ubuntu 20.04, Python 3.8

Problems:
 "Works on my machine" syndrome
 Dependency conflicts
 Environment inconsistencies
 Complex setup procedures
 Difficult to scale
 Resource waste (full VMs)

With Docker containers:

Same application, packaged once:
├─ Container image includes everything
├─ Runs identically everywhere
├─ Isolated from host system
├─ Lightweight (shares kernel)
└─ Deploys in seconds

Result:
 Consistent environments
 Fast deployment
 Easy scaling
 Efficient resource usage
 Portable across clouds

Real stat: 89% of organizations now use containers in production! 📊

Understanding Docker Containers

What is a container?

A container is a lightweight, standalone, executable package that includes everything needed to run software:

Docker Container Structure:

┌─────────────────────────────────────┐
     Your Application (nginx)        
├─────────────────────────────────────┤
     Runtime Dependencies            
     (libraries, configs)            
├─────────────────────────────────────┤
     Base OS (Alpine Linux)          
     (minimal, just essentials)      
├─────────────────────────────────────┤
     Docker Engine                   
├─────────────────────────────────────┤
     Host Operating System           
     (shared kernel)                 
└─────────────────────────────────────┘

vs Traditional VM:

┌─────────────────────────────────────┐
     Your Application                
├─────────────────────────────────────┤
     Dependencies                    
├─────────────────────────────────────┤
     Guest OS (full OS!)             
     (GBs of overhead)               
├─────────────────────────────────────┤
     Hypervisor                      
├─────────────────────────────────────┤
     Host Operating System           
└─────────────────────────────────────┘

Key differences:

  • Containers: Share host kernel, MBs in size, start in milliseconds

  • VMs: Full OS per instance, GBs in size, start in minutes

Why Nginx with Alpine?

Nginx - The web server:

  • High-performance HTTP server

  • Reverse proxy and load balancer

  • Powers 33% of all websites

  • Low memory footprint

  • Event-driven architecture

Alpine Linux - The minimal base:

  • Only ~5 MB base image

  • Security-focused (minimal attack surface)

  • Uses musl libc and busybox

  • Fast downloads and deployments

  • Standard for Docker images

nginx:alpine comparison:

nginx:latest       142 MB
nginx:alpine       23 MB  (6x smaller!)

Benefits:
 Faster pulls (less download time)
 Smaller storage footprint
 Reduced attack surface
 Quicker deployments
 Lower bandwidth costs

Real-World Container Use Cases

Use Case 1: Microservices Architecture

E-commerce Platform:
├─ nginx_frontend (UI)
├─ api_gateway (routing)
├─ user_service (authentication)
├─ product_service (catalog)
├─ order_service (checkout)
└─ payment_service (transactions)

Each in own container, scales independently!

Use Case 2: Development Environments

Developer workflow:
1. Pull container: docker pull nginx:alpine
2. Run locally: docker run nginx:alpine
3. Identical to production!
4. No "works on my machine" issues

Use Case 3: CI/CD Pipelines

Build  Test  Deploy
Each stage in container
Reproducible, isolated builds

🏗️ Understanding the Setup

Application Server 3 Details:

Server: stapp03
User: banner
Password: BigGr33n
Role: Application Server 3

Current State:
├─ Docker installed
├─ Docker daemon running
└─ No nginx container yet

Target State:
├─ Container: nginx_3
├─ Image: nginx:alpine
├─ Status: Running
└─ Port: 80 (default)

Docker Architecture:

Application Server 3 (stapp03)

Docker Engine
├─ Docker Daemon (dockerd)
  └─ Manages containers

├─ Docker CLI (docker command)
  └─ User interface

└─ Container Runtime
   └─ nginx_3 container
      ├─ nginx:alpine image
      ├─ Nginx process (PID 1)
      ├─ Port 80 exposed
      └─ Isolated filesystem

Container Lifecycle:

Image Pull  Container Create  Container Start  Running

1. Pull nginx:alpine
   └─ Downloads from Docker Hub

2. Create nginx_3
   └─ Container created (not running)

3. Start nginx_3
   └─ Nginx process starts

4. Running state
   └─ Ready to serve traffic

🛠️ Complete Step-by-Step Implementation

Phase 1: Access and Verify Server

Step 1.1: SSH to Application Server 3

# Connect to Application Server 3
ssh banner@stapp03
# Password: BigGr33n

You're logged in!

Step 1.2: Verify Docker Installation

# Check Docker version
docker --version

Expected output:

Docker version 24.0.7, build afdd53b

Docker installed!

Step 1.3: Verify Docker Service Running

# Check Docker daemon status
sudo systemctl status docker

Expected output:

 docker.service - Docker Application Container Engine
   Loaded: loaded (/usr/lib/systemd/system/docker.service; enabled)
   Active: active (running) since [timestamp]

Docker daemon active!

If Docker not running:

# Start Docker service
sudo systemctl start docker

# Enable on boot
sudo systemctl enable docker

Step 1.4: Check Existing Containers

# List all containers
sudo docker ps -a

Expected output:

CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

No containers yet - clean slate!

Step 1.5: Verify Docker Hub Access

# Test connectivity to Docker Hub
sudo docker search nginx

Should show nginx images available!

Phase 2: Pull Nginx Alpine Image

Step 2.1: Pull the Image

# Pull nginx with alpine tag
sudo docker pull nginx:alpine

Expected output:

alpine: Pulling from library/nginx
96526aa774ef: Pull complete
b66f8c90f155: Pull complete
e4c6d57711e5: Pull complete
9e7b158d2e68: Pull complete
7f1ff86bec17: Pull complete
ec638d5e6d10: Pull complete
9d880a5f4e3a: Pull complete
bcc29e6aed37: Pull complete
Digest: sha256:a59278fd22a9d411121e190b8cec8aa57b306aa3332459197777583beb728f59
Status: Downloaded newer image for nginx:alpine
docker.io/library/nginx:alpine

Image downloaded! 🎉

What just happened:

  • Connected to Docker Hub (docker.io)

  • Downloaded nginx repository

  • Retrieved alpine tag (latest alpine version)

  • Pulled all layers (8 in this case)

  • Stored in local image cache

Step 2.2: Verify Image Downloaded

# List local images
sudo docker images

Expected output:

REPOSITORY   TAG       IMAGE ID       CREATED        SIZE
nginx        alpine    a99a39d070bf   2 weeks ago    23.5MB

Image ready to use!

Step 2.3: Inspect Image Details

# View detailed image information
sudo docker image inspect nginx:alpine

Shows:

  • Image layers

  • Creation date

  • Environment variables

  • Exposed ports (80, 443)

  • Entry point command

Step 2.4: View Image History

# See how image was built
sudo docker history nginx:alpine

Shows build layers and sizes!

Phase 3: Create and Run Container

Step 3.1: Run Container (Single Command)

# Create and start nginx_3 container
sudo docker run -d --name nginx_3 nginx:alpine

Expected output:

f8e7d9c6b5a4e3d2c1b0a9f8e7d6c5b4a3e2d1c0b9a8f7e6d5c4b3a2e1d0c9b8

Container ID returned - success! 🎊

Command breakdown:

  • docker run = create and start container

  • -d = detached mode (background)

  • --name nginx_3 = container name

  • nginx:alpine = image to use

What happened internally:

  1. Docker checked for nginx:alpine image (found locally)

  2. Created new container from image

  3. Assigned name "nginx_3"

  4. Started container in background

  5. Nginx process started inside container

Step 3.2: Alternative - Create Then Start

# If you want explicit steps:

# Create container (doesn't start)
sudo docker create --name nginx_3 nginx:alpine

# Start the container
sudo docker start nginx_3

Both approaches valid! Our one-liner is faster. ✅

Phase 4: Verify Container Running

Step 4.1: Check Container Status

# List running containers
sudo docker ps

Expected output:

CONTAINER ID   IMAGE          COMMAND                  CREATED         STATUS         PORTS     NAMES
f8e7d9c6b5a4   nginx:alpine   "/docker-entrypoint.…"   30 seconds ago  Up 28 seconds  80/tcp    nginx_3

Container running!

Key information:

  • STATUS: "Up 28 seconds" = running

  • PORTS: "80/tcp" = nginx listening on port 80

  • NAMES: "nginx_3" = our container name

  • COMMAND: Shows entrypoint script

Step 4.2: View All Containers (Including Stopped)

# Include stopped containers
sudo docker ps -a

Should only show nginx_3 (running)

Step 4.3: Check Container Logs

# View nginx logs
sudo docker logs nginx_3

Expected output:

/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
/docker-entrypoint.sh: Sourcing /docker-entrypoint.d/15-local-resolvers.envsh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
/docker-entrypoint.sh: Configuration complete; ready for start up

Nginx started successfully!

Step 4.4: Inspect Container Details

# Get detailed container information
sudo docker inspect nginx_3

Shows:

  • Container ID (full)

  • Network settings

  • IP address

  • Mounted volumes

  • Environment variables

  • State information

Step 4.5: Check Container Stats

# View resource usage
sudo docker stats nginx_3 --no-stream

Expected output:

CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT     MEM %     NET I/O     BLOCK I/O   PIDS
f8e7d9c6b5a4   nginx_3   0.00%     3.5MiB / 7.77GiB     0.04%     656B / 0B   0B / 0B     3

Resource usage minimal!

Phase 5: Test Nginx Functionality

Step 5.1: Get Container IP Address

# Extract container IP
sudo docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' nginx_3

Expected output (example):

172.17.0.2

Container has IP address!

Step 5.2: Test Nginx from Host

# Curl the container IP
curl http://172.17.0.2

Expected output (HTML):

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
...
</body>
</html>

Nginx serving pages! 🎉

Step 5.3: Check Nginx Process Inside Container

# Execute command inside container
sudo docker exec nginx_3 ps aux

Expected output:

PID   USER     TIME  COMMAND
    1 root      0:00 nginx: master process nginx -g daemon off;
   29 nginx     0:00 nginx: worker process
   30 nginx     0:00 nginx: worker process

Nginx processes running!

Step 5.4: Verify Nginx Configuration

# Check nginx config inside container
sudo docker exec nginx_3 nginx -t

Expected output:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Configuration valid!

Step 5.5: Test with Head Request

# Check HTTP headers
curl -I http://172.17.0.2

Expected output:

HTTP/1.1 200 OK
Server: nginx/1.25.3
Date: [current date]
Content-Type: text/html
Content-Length: 615
Last-Modified: [date]
Connection: keep-alive
ETag: "..."
Accept-Ranges: bytes

HTTP 200 - server responding!

Phase 6: Final Verification

Step 6.1: Confirm Container Name

# Verify exact name
sudo docker ps --filter "name=nginx_3" --format "table {{.Names}}\t{{.Status}}"

Expected output:

NAMES     STATUS
nginx_3   Up 5 minutes

Correct name and running!

Step 6.2: Verify Image Tag

# Check which image version
sudo docker inspect nginx_3 | grep "Image"

Should show nginx:alpine!

Step 6.3: Check Container Health

# View container state
sudo docker inspect nginx_3 | grep "Status"

Expected output:

"Status": "running",

Container healthy!

Step 6.4: Verify Restart Policy

# Check restart behavior
sudo docker inspect nginx_3 | grep -A 2 "RestartPolicy"

Shows restart configuration

Step 6.5: Final Status Check

# Complete overview
sudo docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}"

Expected output:

NAMES     IMAGE          STATUS         PORTS
nginx_3   nginx:alpine   Up 10 minutes  80/tcp

ALL REQUIREMENTS MET! 🎉🎊🎈

🔍 Understanding What We Accomplished

Container Architecture

What exists now:

Application Server 3 (stapp03)

Host OS (CentOS/RHEL)
├─ Docker Engine
  └─ Container: nginx_3
     ├─ Image: nginx:alpine (23.5 MB)
     ├─ Process: nginx master (PID 1)
     ├─ Workers: nginx workers
     ├─ Network: bridge (172.17.0.2)
     ├─ Filesystem: isolated
     └─ State: Running

└─ Port 80 (inside container)

Docker Run Command Explained

Our command: sudo docker run -d --name nginx_3 nginx:alpine

What each part does:

docker run
└─ Create and start container

-d (--detach)
└─ Run in background
└─ Returns container ID
└─ Terminal not attached

--name nginx_3
└─ Give container a name
└─ Instead of random name
└─ Easier to reference

nginx:alpine
└─ Image to use
└─ Format: repository:tag
└─ Pulls if not local

Additional common flags (not used but useful):

-p 8080:80
└─ Port mapping: host:container
└─ Access on http://server:8080

-v /host/path:/container/path
└─ Mount volume
└─ Persist data

-e KEY=value
└─ Environment variable
└─ Configuration

--restart=always
└─ Auto-restart container
└─ On failure or reboot

--rm
└─ Remove container when stops
└─ For temporary containers

Container vs Image

Crucial difference:

Image (nginx:alpine)
├─ Read-only template
├─ Stored in /var/lib/docker/image
├─ Can create many containers
├─ Like a class in OOP
└─ 23.5 MB

Container (nginx_3)
├─ Running instance of image
├─ Has writeable layer
├─ Has state (running/stopped)
├─ Like an object in OOP
└─ Process with PID

Analogy:

  • Image = Recipe

  • Container = Cake made from recipe

You can make many cakes (containers) from one recipe (image)!

Container Lifecycle States

Created  Starting  Running  Stopping  Stopped  Removed

Created:
└─ docker create nginx_3
└─ Exists but not running

Running:
└─ docker start nginx_3
└─ Process active

Stopped:
└─ docker stop nginx_3
└─ Process terminated, container remains

Removed:
└─ docker rm nginx_3
└─ Container deleted

Networking Explained

Default bridge network:

Docker Host
├─ docker0 bridge (172.17.0.1)

├─ Container: nginx_3 (172.17.0.2)
  └─ Can reach: other containers, internet

└─ Host processes
   └─ Can reach: 172.17.0.2 directly

External world:
└─ Cannot reach container (no port mapping)

To expose externally:

# Would need port mapping:
docker run -d -p 8080:80 --name nginx_3 nginx:alpine
# Then accessible at: http://stapp03:8080

💡 Key Takeaways

Containers are lightweight - only 23.5 MB for nginx:alpine

Docker run creates and starts in one command

-d flag runs detached (background process)

--name assigns custom name (instead of random)

Alpine images are minimal (security and size)

Containers are isolated (own filesystem, processes)

docker ps shows running containers only

docker ps -a shows all including stopped

docker logs shows output from container

docker exec runs commands inside container

Container ≠ Image (instance vs template)

🎓 Interview Questions

Q1: What's the difference between a Docker image and a Docker container?

Answer: Image is template, container is running instance. Image: Read-only layers, blueprint for containers, stored once, shared by many containers, created from Dockerfile. Example: nginx:alpine image (23.5 MB). Container: Running instance of image, has writeable layer on top, independent lifecycle, isolated process namespace, can have multiple from same image. Example: nginx_3 container from nginx:alpine. Analogy: Image = ISO file, Container = running virtual machine. Or Image = Class, Container = Object. In practice: docker pull nginx:alpine downloads image, docker run nginx:alpine creates container. One image can spawn hundreds of containers.

Q2: Explain what happens when you run docker run -d --name nginx_3 nginx:alpine.

Answer: Multi-step process: 1) Image check: Docker checks if nginx:alpine exists locally. If not, pulls from Docker Hub. 2) Container creation: Creates new container with name nginx_3, assigns unique container ID, sets up container filesystem (layered with image). 3) Network setup: Attaches to default bridge network, assigns IP (e.g., 172.17.0.2). 4) Start process: Runs container's entrypoint (nginx), PID 1 inside container. 5) Detached mode: Returns control to terminal immediately, container runs in background. Result: Container running, nginx serving on port 80 (internal), accessible via container IP. Can manage with: docker stop nginx_3, docker start nginx_3, docker rm nginx_3.

Q3: Why use Alpine Linux for Docker images? What are trade-offs?

Answer: Alpine prioritizes small size and security. Advantages: Size (5 MB vs 100+ MB for Ubuntu), security (minimal packages = smaller attack surface), speed (faster downloads, quicker deployments), efficiency (more containers per host). Disadvantages: Uses musl libc not glibc (compatibility issues), missing common tools (bash, vim need installation), some binary packages incompatible, less familiar for developers used to Debian/Ubuntu. Best for: Production containers, microservices, CI/CD pipelines, cloud deployments. Avoid when: Need specific glibc dependencies, complex system tools required, team unfamiliar with Alpine. Comparison: nginx:latest = 142 MB, nginx:alpine = 23.5 MB (6x smaller!). For our use case (simple nginx), Alpine perfect choice.

Q4: How would you troubleshoot if the container starts but nginx doesn't respond?

Answer: Systematic debugging approach: 1) Check container running: docker ps (verify status = "Up"). 2) View logs: docker logs nginx_3 (errors in startup?). 3) Exec into container: docker exec -it nginx_3 sh, then ps aux (nginx processes running?), nginx -t (config valid?). 4) Check networking: docker inspect nginx_3 | grep IPAddress, curl http://<container-ip> (reachable?). 5) Verify ports: docker port nginx_3 (correct ports?). 6) Check resources: docker stats nginx_3 (CPU/memory issues?). 7) Inspect events: docker events (system events). Common issues: Port conflict (another service on 80), configuration error (bad nginx.conf), permission issues (file ownership), resource limits (out of memory). Solution pattern: Check logs first (80% of issues visible there), then process/network, finally resources.

Q5: What's the difference between docker stop and docker kill?

Answer: Graceful vs forceful shutdown. docker stop: Sends SIGTERM (signal 15) to container, allows cleanup (close connections, save state, flush buffers), waits 10 seconds (default grace period), if still running, sends SIGKILL. Use for: Production containers, database containers, any container needing graceful shutdown. docker kill: Immediately sends SIGKILL (signal 9), no cleanup opportunity, instant termination. Use for: Frozen containers, emergency situations, testing failure scenarios. Example: docker stop nginx_3 - nginx finishes serving current requests, closes connections, shuts down cleanly. docker kill nginx_3 - nginx terminated mid-request, possible data loss. Best practice: Always use docker stop unless container unresponsive. Can customize grace period: docker stop -t 30 nginx_3 (30 second wait).

Q6: How do you persist data in containers? Why is it important?

Answer: Containers are ephemeral - data lost when removed. Problem: Container writes to /var/log/nginx, docker rm nginx_3 deletes logs. Solutions: 1) Volumes (recommended): docker run -v nginx-logs:/var/log/nginx nginx:alpine. Docker manages storage, persists across containers, can be backed up. 2) Bind mounts: docker run -v /host/logs:/var/log/nginx nginx:alpine. Maps host directory, direct host access, less portable. 3) tmpfs mounts: docker run --tmpfs /tmp nginx:alpine. Memory storage, fast but volatile. Why important: Databases need persistent storage, logs for debugging, configuration files, user uploads. Best practices: Use named volumes for application data, bind mounts for development, never store data in container layer. Our nginx_3: Doesn't persist data (demo only). Production would need: docker run -d -v nginx-logs:/var/log/nginx -v nginx-conf:/etc/nginx --name nginx_3 nginx:alpine.

🌟 Container Management Commands

Basic Operations:

# Create and start
docker run -d --name nginx_3 nginx:alpine

# Stop container
docker stop nginx_3

# Start stopped container
docker start nginx_3

# Restart container
docker restart nginx_3

# Remove container (must be stopped)
docker rm nginx_3

# Force remove running container
docker rm -f nginx_3

Inspection Commands:

# List running containers
docker ps

# List all containers
docker ps -a

# View logs
docker logs nginx_3

# Follow logs (real-time)
docker logs -f nginx_3

# View resource usage
docker stats nginx_3

# Detailed info
docker inspect nginx_3

Interaction Commands:

# Execute command
docker exec nginx_3 ls -la

# Interactive shell
docker exec -it nginx_3 sh

# Copy file to container
docker cp file.txt nginx_3:/tmp/

# Copy from container
docker cp nginx_3:/etc/nginx/nginx.conf .

🚀 Advanced Scenarios

Scenario 1: Port Mapping for External Access

# Expose nginx on host port 8080
docker run -d -p 8080:80 --name nginx_3 nginx:alpine

# Access from anywhere
curl http://stapp03:8080

Scenario 2: Custom Configuration

# Mount custom nginx.conf
docker run -d \
  -v /path/to/nginx.conf:/etc/nginx/nginx.conf:ro \
  --name nginx_3 \
  nginx:alpine

Scenario 3: Environment Variables

# Pass environment variables
docker run -d \
  -e NGINX_HOST=example.com \
  -e NGINX_PORT=80 \
  --name nginx_3 \
  nginx:alpine

Scenario 4: Resource Limits

# Limit CPU and memory
docker run -d \
  --cpus="0.5" \
  --memory="256m" \
  --name nginx_3 \
  nginx:alpine

Scenario 5: Health Checks

# Add health check
docker run -d \
  --health-cmd="curl -f http://localhost/ || exit 1" \
  --health-interval=30s \
  --health-timeout=3s \
  --health-retries=3 \
  --name nginx_3 \
  nginx:alpine

Scenario 6: Auto-Restart

# Always restart on failure
docker run -d \
  --restart=always \
  --name nginx_3 \
  nginx:alpine

🎯 Production Best Practices

1. Always use specific tags:

# Good
docker run nginx:1.25.3-alpine

# Bad (unpredictable)
docker run nginx:latest

2. Implement health checks:

# Ensure container health monitored
--health-cmd="..."

3. Set resource limits:

# Prevent resource exhaustion
--cpus="1.0" --memory="512m"

4. Use restart policies:

# Auto-recovery from failures
--restart=unless-stopped

5. Mount logs externally:

# Persistent logging
-v /var/log/nginx:/var/log/nginx

🎉 Final Thoughts

You've successfully deployed a containerized application! This is the foundation of modern DevOps:

What you accomplished:

  • ✅ Deployed nginx container on Application Server 3

  • ✅ Used minimal Alpine Linux base (23.5 MB!)

  • ✅ Container running and serving web traffic

  • ✅ Verified functionality with multiple checks

  • ✅ Understood container vs image concepts

Real-world impact:

  • Fast deployment: Seconds instead of hours

  • Consistency: Identical environments everywhere

  • Scalability: Easily replicate containers

  • Efficiency: Multiple containers per host

  • Portability: Run anywhere Docker runs

This is modern infrastructure! Companies run thousands of containers like this in production! 💪

🚀 What's Next?

Day 36 complete! 🎉 You've mastered Docker container deployment!

Skills Mastered Today:

  • ✅ Docker container creation

  • ✅ Running nginx with Alpine

  • ✅ Container verification and inspection

  • ✅ Basic troubleshooting

  • ✅ Understanding container architecture

Coming up: More Docker adventures - multi-container applications, Docker Compose, container orchestration with Kubernetes!


Day: 36/100
Challenge: KodeKloud Cloud DevOps
Date: December 12, 2025
Topic: Docker Nginx Container Deployment

How many containers do you run in production? What's your container strategy? Share your Docker journey! 🐳

More from this blog

🚀 DevOps Challenge- KodeKloud Solutions

73 posts