Complete Docker Container Deployment Guide: From Installation to Production Best Practices
This guide walks you through every step of Docker container deployment, covering installation, environment requirements, daemon configuration, Dockerfile best practices, multi‑stage builds, Compose orchestration, security hardening, resource limits, monitoring, troubleshooting, and production‑grade recommendations to ensure reliable, scalable services.
Overview
Docker packages an application and its dependencies into an immutable image, guaranteeing identical environments for development, testing, and production. Containers start in 0.5‑2 seconds and share read‑only layers, reducing storage and network transfer.
Key Characteristics
Environment consistency : identical images for dev and prod.
Second‑level startup : containers start 10‑100× faster than VMs.
Layered storage : shared read‑only layers save space.
Resource isolation : cgroup and namespace isolation.
Declarative orchestration : Dockerfile and docker-compose.yml define reproducible environments.
Installation
System checks
# Check OS version
cat /etc/os-release
# Check kernel version (recommend 5.x+)
uname -r
# Verify overlay2 support
cat /proc/filesystems | grep overlay
# Disk space
df -h
# Existing Docker version
docker --version || echo "Docker not installed"Uninstall old Docker
# Ubuntu/Debian
sudo apt remove -y docker docker-engine docker.io containerd runc
sudo apt autoremove -y
# CentOS/Rocky
sudo yum remove -y docker docker-client docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engineRemoving old packages does not delete /var/lib/docker/ data; delete manually if a clean install is required.
Install Docker CE (Ubuntu/Debian)
# Install prerequisites
sudo apt update
sudo apt install -y ca-certificates curl gnupg lsb-release
# Add Docker GPG key
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
# Add Docker repository
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# Install Docker
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# Verify
docker --version
docker compose versionInstall Docker CE (CentOS/Rocky)
# Install yum-utils
sudo yum install -y yum-utils
# Add Docker repo
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
# Install Docker
sudo yum install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# Enable and start
sudo systemctl start docker
sudo systemctl enable docker
# Verify
docker --version
docker compose versionConfigure non‑root user
# Create docker group (usually created automatically)
sudo groupadd docker 2>/dev/null
# Add current user to docker group
sudo usermod -aG docker $USER
# Apply without logout
newgrp docker
# Verify
docker run hello-worldWarning: Members of the docker group have root‑equivalent privileges inside containers.
Daemon configuration (daemon.json)
{
"storage-driver": "overlay2",
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "3"
},
"registry-mirrors": [
"https://mirror.ccs.tencentyun.com",
"https://docker.mirrors.ustc.edu.cn"
],
"insecure-registries": [
"harbor.internal.example.com:5000"
],
"live-restore": true,
"default-ulimits": {
"nofile": { "Name": "nofile", "Hard": 65535, "Soft": 65535 }
},
"bip": "172.17.0.1/16",
"default-address-pools": [
{ "base": "172.18.0.0/16", "size": 24 }
],
"max-concurrent-downloads": 10,
"max-concurrent-uploads": 5,
"features": { "buildkit": true },
"data-root": "/var/lib/docker",
"dns": ["8.8.8.8","114.114.114.114"]
}Key points: use overlay2 storage, limit log size to avoid disk exhaustion, enable live-restore so containers survive daemon restarts, and configure a custom bridge network to avoid IP conflicts.
Dockerfile best practices
Multi‑stage builds
Multi‑stage builds dramatically shrink image size. Example for a Go application reduces a 1.3 GB image to 15 MB.
# Dockerfile for Go
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /app/server ./cmd/server
FROM alpine:3.19
RUN apk --no-cache add ca-certificates tzdata
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app
COPY --from=builder /app/server .
COPY --from=builder /app/configs ./configs
ENV TZ=Asia/Shanghai
USER appuser
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget -qO- http://localhost:8080/health || exit 1
ENTRYPOINT ["/app/server"]Result: without multi‑stage the image is ~1.3 GB; with multi‑stage it is 15 MB.
Java Spring Boot example (layered extraction) reduces a 900 MB image to ~180 MB.
# Dockerfile for Java
FROM maven:3.9-eclipse-temurin-21 AS builder
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline -B
COPY src ./src
RUN mvn package -DskipTests -B
RUN java -Djarmode=layertools -jar target/*.jar extract --destination /extracted
FROM eclipse-temurin:21-jre-alpine
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app
COPY --from=builder /extracted/dependencies/ ./
COPY --from=builder /extracted/spring-boot-loader/ ./
COPY --from=builder /extracted/snapshot-dependencies/ ./
COPY --from=builder /extracted/application/ ./
ENV TZ=Asia/Shanghai
ENV JAVA_OPTS="-Xms512m -Xmx512m -XX:+UseG1GC"
USER appuser
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \
CMD wget -qO- http://localhost:8080/actuator/health || exit 1
ENTRYPOINT ["sh","-c","java $JAVA_OPTS org.springframework.boot.loader.launch.JarLauncher"].dockerignore
.git
.gitignore
Dockerfile
docker-compose*.yml
README.md
LICENSE
docs/
node_modules
npm-debug.log
vendor/
target/
*.jar
*.class
__pycache__
*.pyc
.venv
venv
.idea
.vscode
*.swp
*.swo
.env
.env.*Including .env in the image is prohibited because it may contain database passwords.
Guidelines
COPY vs ADD : prefer COPY for predictable behavior.
Combine RUN statements to reduce layers.
Run as non‑root to mitigate security risks.
HEALTHCHECK enables Docker and orchestrators to detect unhealthy containers.
ARG vs ENV : use ARG for build‑time values, ENV for runtime configuration.
Docker Compose orchestration
Example compose file defines Nginx reverse proxy, Node.js API, MySQL, and Redis with health checks, resource limits, and custom networks.
# docker-compose.yml
services:
nginx:
image: nginx:1.24-alpine
container_name: app-nginx
ports: ["80:80","443:443"]
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- ./nginx/ssl:/etc/nginx/ssl:ro
- ./frontend/dist:/var/www/html:ro
- nginx-logs:/var/log/nginx
depends_on:
api:
condition: service_healthy
restart: unless-stopped
deploy:
resources:
limits:
cpus: '1.0'
memory: 256M
networks: [frontend]
api:
build:
context: ./api
dockerfile: Dockerfile
container_name: app-api
env_file: [.env]
environment:
- NODE_ENV=production
- DB_HOST=mysql
- DB_PORT=3306
- REDIS_HOST=redis
- REDIS_PORT=6379
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_healthy
healthcheck:
test: ["CMD","wget","-qO-","http://localhost:3000/health"]
interval: 15s
timeout: 5s
retries: 3
start_period: 10s
restart: unless-stopped
deploy:
resources:
limits:
cpus: '2.0'
memory: 1G
networks: [frontend,backend]
mysql:
image: mysql:8.0
container_name: app-mysql
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
volumes:
- mysql-data:/var/lib/mysql
- ./mysql/conf.d:/etc/mysql/conf.d:ro
- ./mysql/init:/docker-entrypoint-initdb.d:ro
healthcheck:
test: ["CMD","mysqladmin","ping","-h","localhost","-u","root","-p${MYSQL_ROOT_PASSWORD}"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
restart: unless-stopped
deploy:
resources:
limits:
cpus: '2.0'
memory: 2G
networks: [backend]
redis:
image: redis:7-alpine
container_name: app-redis
command: |
redis-server --requirepass ${REDIS_PASSWORD} \
--maxmemory 512mb --maxmemory-policy allkeys-lru \
--appendonly yes
volumes:
- redis-data:/data
healthcheck:
test: ["CMD","redis-cli","-a","${REDIS_PASSWORD}","ping"]
interval: 10s
timeout: 3s
retries: 3
restart: unless-stopped
deploy:
resources:
limits:
cpus: '1.0'
memory: 768M
networks: [backend]
networks:
frontend:
driver: bridge
backend:
driver: bridge
internal: true
volumes:
mysql-data:
redis-data:
nginx-logs:Network modes
bridge : default mode, containers communicate via a virtual bridge; suitable for most scenarios.
host : container shares the host network stack; used for high‑performance monitoring.
none : no network, complete isolation; for security‑sensitive offline tasks.
overlay : cross‑host communication in Docker Swarm clusters.
macvlan : container gets its own MAC address on the physical network; needed for legacy networks requiring independent IPs.
Volume types
Named volume (e.g., -v mydata:/data) stored under Docker‑managed /var/lib/docker/volumes/; ideal for databases and persistent data.
Bind mount (e.g., -v /host/path:/container/path) maps a host directory; useful for config files and hot‑reload code.
tmpfs (e.g., --tmpfs /tmp) stores data in memory; suitable for temporary or sensitive data that must not persist on disk.
Best practices and pitfalls
Image slimming
Choose appropriate base images: ubuntu:22.04 (77 MB), debian:bookworm-slim (74 MB), alpine:3.19 (7 MB), distroless (2‑20 MB), scratch (0 MB).
Use dive to analyze layer size; a Java image was reduced from 600 MB to 180 MB.
Security hardening
Create a non‑root user in the Dockerfile and switch to it:
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuserRun containers with --read-only and mount writable paths via tmpfs or volumes.
Drop all Linux capabilities and add only required ones, e.g. --cap-drop ALL --cap-add NET_BIND_SERVICE.
Scan images with trivy; enforce failure on HIGH/CRITICAL findings.
trivy image --severity HIGH,CRITICAL myapp:latestLog management
Global json-file driver with max-size=100m and max-file=3 prevents disk exhaustion.
Per‑service overrides possible in docker-compose.yml.
services:
api:
logging:
driver: json-file
options:
max-size: "200m"
max-file: "5"Centralized logging via the fluentd driver.
services:
api:
logging:
driver: fluentd
options:
fluentd-address: "10.0.0.50:24224"
tag: "docker.{{.Name}}"
fluentd-async: "true"Resource limits
Running a container without limits can let a memory leak consume the host. Example command with limits:
docker run -d \
--name myapp \
--memory 1g \
--memory-swap 1g \
--cpus 2.0 \
--pids-limit 200 \
--oom-score-adj 500 \
myapp:latest --memory 1g: sets a 1 GB memory cap. --memory-swap 1g: disables swap for the container. --cpus 2.0: limits CPU usage to two cores. --pids-limit 200: caps the number of processes. --oom-score-adj 500: gives the container a higher OOM kill priority.
CI/CD integration
Enable BuildKit ( DOCKER_BUILDKIT=1) and use cache mounts for Go modules:
# Dockerfile snippet
RUN --mount=type=cache,target=/go/pkg/mod go mod download
RUN --mount=type=cache,target=/root/.cache/go-build go build -o /app/serverMulti‑platform builds with docker buildx for amd64 and arm64:
docker buildx create --name multiarch --use
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t harbor.example.com/myapp:v1.2.3 \
--push .Push images to a private Harbor registry:
docker login harbor.internal.example.com:5000
docker tag myapp:v1.2.3 harbor.internal.example.com:5000/project/myapp:v1.2.3
docker push harbor.internal.example.com:5000/project/myapp:v1.2.3Troubleshooting and monitoring
Common issues
Cannot connect to the Docker daemon – start the daemon ( systemctl start docker) or add the user to the docker group.
No space left on device – prune unused images/containers ( docker system prune -a) and enforce log size limits.
OCI runtime create failed – upgrade kernel to 5.x+ or adjust the seccomp profile.
Port is already allocated – identify the occupying process with ss -tlnp | grep <port>.
Network has active endpoints – stop and remove containers before deleting the network.
Image pull timeout – verify DNS configuration or use registry mirrors.
Performance monitoring
docker statsprovides real‑time CPU, memory, network, and block I/O per container.
Long‑term monitoring stack: cAdvisor collects container metrics, Prometheus stores them, and Grafana visualizes. Example docker-compose.monitoring.yml includes cAdvisor, Prometheus, and Grafana services.
Prometheus scrape config for cAdvisor:
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'cadvisor'
static_configs:
- targets: ['cadvisor:8080']Typical alert thresholds: CPU > 85 %, memory > 90 %, container restarts > 3 /h, disk usage > 85 %.
Backup and recovery
A backup script iterates over named volumes, creates tar.gz archives, and backs up compose files. Restoration involves stopping services, extracting the archives back into the volumes, and bringing the stack up.
# docker-backup.sh (simplified)
BACKUP_DIR="/data/backups/docker"
DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p "$BACKUP_DIR"
for vol in $(docker volume ls -q); do
echo "Backing up volume: $vol"
docker run --rm -v "$vol:/source:ro" -v "$BACKUP_DIR:/backup" alpine \
tar czf "/backup/${vol}_$DATE.tar.gz" -C /source .
done
# Backup compose files
tar czf "$BACKUP_DIR/compose_configs_$DATE.tar.gz" /opt/apps/*/docker-compose.yml /opt/apps/*/.env 2>/dev/null
# Delete backups older than 7 days
find "$BACKUP_DIR" -name "*.tar.gz" -mtime +7 -deleteConclusion
The guide consolidates installation, daemon tuning, Dockerfile optimization, Compose orchestration, security hardening, resource limiting, CI/CD practices, monitoring, and disaster recovery into a production‑ready Docker workflow.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Raymond Ops
Linux ops automation, cloud-native, Kubernetes, SRE, DevOps, Python, Golang and related tech discussions.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
