Docker Containerization Workflow for Production: Build, Ship, and Run

Why Docker for Production Workloads?
Docker containers package your application and its dependencies into a single portable unit that runs consistently across development, staging, and production environments. For teams deploying on ServerRaja cloud servers, Docker eliminates the classic problem of "it works on my machine" and enables rapid scaling.
This guide covers the complete production Docker workflow, from writing optimized Dockerfiles to running containers securely in production.
Writing Production-Grade Dockerfiles
Multi-Stage Builds
Multi-stage builds dramatically reduce final image size by separating the build environment from the runtime environment:
# Stage 1: Build
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force
COPY . .
RUN npm run build# Stage 2: Production FROM node:20-alpine AS production RUN addgroup -g 1001 appgroup && adduser -u 1001 -G appgroup -s /bin/sh -D appuser WORKDIR /app COPY --from=builder --chown=appuser:appgroup /app/dist ./dist COPY --from=builder --chown=appuser:appgroup /app/node_modules ./node_modules COPY --from=builder --chown=appuser:appgroup /app/package.json ./
USER appuser EXPOSE 3000 HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1 CMD ["node", "dist/server.js"] ```
Optimizing Image Layers
Order your Dockerfile instructions from least to most frequently changing:
FROM python:3.12-slimWORKDIR /app
# Install system dependencies (rarely changes) RUN apt-get update && \ apt-get install -y --no-install-recommends \ libpq-dev \ && rm -rf /var/lib/apt/lists/*
# Install Python dependencies (changes sometimes) COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt
# Copy application code (changes frequently) COPY . .
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "--workers", "4", "app:app"] ```
Docker Compose for Multi-Service Applications
Define your entire application stack with Docker Compose:
version: '3.8'services: app: build: context: . dockerfile: Dockerfile ports: - "3000:3000" environment: - NODE_ENV=production - DATABASE_URL=postgresql://user:pass@db:5432/myapp - REDIS_URL=redis://cache:6379 depends_on: db: condition: service_healthy cache: condition: service_started restart: unless-stopped deploy: resources: limits: cpus: '2.0' memory: 1G reservations: cpus: '0.5' memory: 256M
db: image: postgres:16-alpine volumes: - postgres_data:/var/lib/postgresql/data environment: POSTGRES_DB: myapp POSTGRES_USER: user POSTGRES_PASSWORD: pass healthcheck: test: ["CMD-SHELL", "pg_isready -U user -d myapp"] interval: 10s timeout: 5s retries: 5
cache: image: redis:7-alpine command: redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru
nginx: image: nginx:alpine ports: - "80:80" - "443:443" volumes: - ./nginx.conf:/etc/nginx/nginx.conf:ro - ./certs:/etc/nginx/certs:ro depends_on: - app
volumes: postgres_data: ```
Security Hardening
Run as Non-Root User
Always create and switch to a non-root user in your Dockerfiles:
RUN groupadd -r appuser && useradd -r -g appuser appuser
USER appuser
Scan Images for Vulnerabilities
Use Trivy to scan your images before deploying:
# Install Trivy
sudo apt-get install trivy# Scan your image trivy image myapp:latest
# Fail build on critical vulnerabilities trivy image --exit-code 1 --severity CRITICAL myapp:latest ```
Read-Only Filesystem
Run containers with read-only filesystems where possible:
services:
app:
image: myapp:latest
read_only: true
tmpfs:
- /tmp
- /app/cache
Container Registry Workflow
Push your images to a private registry:
# Tag the image
docker tag myapp:latest registry.serverraja.com/myapp:v1.2.3# Push to registry docker push registry.serverraja.com/myapp:v1.2.3
# Pull on production server docker pull registry.serverraja.com/myapp:v1.2.3
# Run the container docker run -d \ --name myapp \ --restart unless-stopped \ -p 3000:3000 \ --memory 1g \ --cpus 2 \ registry.serverraja.com/myapp:v1.2.3 ```
Health Checks and Logging
Implement proper health checks in your application and configure Docker logging:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
Set this in `/etc/docker/daemon.json` on your production server and restart Docker:
sudo systemctl restart docker
Monitor container health:
# View container status
docker inspect --format='{{.State.Health.Status}}' myapp# View container logs docker logs -f --tail 100 myapp
# View resource usage docker stats myapp ```
Zero-Downtime Updates
Use rolling updates to avoid downtime:
# Pull new image
docker pull registry.serverraja.com/myapp:v1.2.4# Start new container alongside old one docker run -d --name myapp-new -p 3001:3000 registry.serverraja.com/myapp:v1.2.4
# Verify new container is healthy curl http://localhost:3001/health
# Update nginx upstream and reload sudo nginx -s reload
# Stop old container docker stop myapp && docker rm myapp ```
Conclusion
A solid Docker containerization workflow covers writing optimized Dockerfiles, managing multi-service stacks with Compose, hardening security, and implementing zero-downtime deployments. Start with multi-stage builds and non-root users, then layer on vulnerability scanning and registry workflows as your team matures. ServerRaja cloud servers provide the performance and reliability your containerized workloads need.