name
docker
description
Docker - containers, Dockerfile, docker-compose, multi-stage builds, production, CI/CD
metadata
author
version
tags
mte90
1.0.0
docker
docker-compose
containerization
deployment
ci-cd
containers
Complete guide to containerization with Docker - images, containers, compose, and best practices.
FROM python:3.12-slim
WORKDIR /app
# Install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy application
COPY . .
# Run
CMD ["python" , "manage.py" , "runserver" , "0.0.0.0:8000" ]
Production Dockerfile (Multi-stage)
# Build stage
FROM python:3.12-slim AS builder
WORKDIR /app
# Install build dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
libpq-dev \
&& rm -rf /var/lib/apt/lists/*
# Create virtual environment
RUN python -m venv /venv
ENV PATH="/venv/bin:$PATH"
# Install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Production stage
FROM python:3.12-slim
# Install runtime dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
libpq5 \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
# Copy virtual environment
COPY --from=builder /venv /venv
ENV PATH="/venv/bin:$PATH"
# Copy application
COPY --from=builder /app /app
# Create non-root user
RUN adduser --disabled-password --gecos '' appuser && \
chown -R appuser:appuser /app
USER appuser
# Expose port
EXPOSE 8000
# Run
CMD ["gunicorn" , "myproject.wsgi:application" , "--bind" , "0.0.0.0:8000" ]
# requirements.txt
Django>=5.0
gunicorn>=21.0
psycopg2-binary>=2.9
redis>=5.0
django-redis>=0.9
celery>=5.3
version : ' 3.8'
services :
web :
build : .
ports :
- " 8000:8000"
volumes :
- .:/app
environment :
- DEBUG=1
- DATABASE_URL=postgres://postgres:postgres@db:5432/mydb
- REDIS_URL=redis://redis:6379/0
depends_on :
- db
- redis
db :
image : postgres:16
environment :
- POSTGRES_DB=mydb
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
volumes :
- postgres_data:/var/lib/postgresql/data
ports :
- " 5432:5432"
redis :
image : redis:7-alpine
ports :
- " 6379:6379"
celery :
build : .
command : celery -A myproject worker -l info
volumes :
- .:/app
environment :
- DATABASE_URL=postgres://postgres:postgres@db:5432/mydb
- REDIS_URL=redis://redis:6379/0
depends_on :
- db
- redis
volumes :
postgres_data :
version : ' 3.8'
services :
web :
build :
context : .
dockerfile : Dockerfile.prod
ports :
- " 8000:8000"
environment :
- DEBUG=0
- DATABASE_URL=postgres://user:password@db:5432/mydb
- REDIS_URL=redis://redis:6379/0
depends_on :
db :
condition : service_healthy
redis :
condition : service_started
restart : unless-stopped
db :
image : postgres:16
environment :
- POSTGRES_DB=mydb
- POSTGRES_USER=user
- POSTGRES_PASSWORD=password
volumes :
- postgres_data:/var/lib/postgresql/data
healthcheck :
test : ["CMD-SHELL", "pg_isready -U user -d mydb"]
interval : 10s
timeout : 5s
retries : 5
restart : unless-stopped
redis :
image : redis:7-alpine
restart : unless-stopped
celery :
build :
context : .
dockerfile : Dockerfile.prod
command : celery -A myproject worker -l info
environment :
- DATABASE_URL=postgres://user:password@db:5432/mydb
- REDIS_URL=redis://redis:6379/0
depends_on :
- db
- redis
restart : unless-stopped
celery-beat :
build :
context : .
dockerfile : Dockerfile.prod
command : celery -A myproject beat -l info
environment :
- DATABASE_URL=postgres://user:password@db:5432/mydb
- REDIS_URL=redis://redis:6379/0
depends_on :
- db
- redis
restart : unless-stopped
volumes :
postgres_data :
# Build
docker build -t myapp .
# Run
docker run -p 8000:8000 myapp
# With docker-compose
docker-compose up -d
docker-compose up -d --build
docker-compose logs -f web
docker-compose exec web python manage.py migrate
# Stop
docker-compose down
docker-compose down -v # Remove volumes
# .github/workflows/docker.yml
name : Docker
on :
push :
branches : [main]
tags : ['v*']
jobs :
build :
runs-on : ubuntu-latest
steps :
- uses : actions/checkout@v4
- name : Set up Docker Buildx
uses : docker/setup-buildx-action@v3
- name : Login to Docker Hub
uses : docker/login-action@v3
with :
username : ${{ secrets.DOCKER_USERNAME }}
password : ${{ secrets.DOCKER_PASSWORD }}
- name : Extract metadata
id : meta
uses : docker/metadata-action@v5
with :
images : myuser/myapp
tags : |
type=ref,event=branch
type=ref,event=tag
type=sha
- name : Build and push
uses : docker/build-push-action@v5
with :
context : .
push : ${{ github.event_name != 'pull_request' }}
tags : ${{ steps.meta.outputs.tags }}
labels : ${{ steps.meta.outputs.labels }}
cache-from : type=registry,ref=myuser/myapp:buildcache
cache-to : type=registry,ref=myuser/myapp:buildcache,mode=max
Use multi-stage builds for smaller images
Don't run as root - use USER instruction
Use .dockerignore to exclude unnecessary files
Use healthchecks for dependencies
Use volume mounts for development
Set proper environment variables
Use gunicorn in production, not runserver
Scan images for vulnerabilities - docker scout
Use specific tags - not :latest in production
Readonly containers - use --read-only flag
# docker-compose.yml
version : ' 3.8'
services :
web :
build : .
ports :
- " 8000:8000"
environment :
- DEBUG=1
depends_on :
- db
- redis
volumes :
- .:/app
restart : unless-stopped
db :
image : postgres:15-alpine
volumes :
- postgres_data:/var/lib/postgresql/data
environment :
- POSTGRES_DB=app
- POSTGRES_USER=user
- POSTGRES_PASSWORD=secret
redis :
image : redis:7-alpine
volumes :
- redis_data:/data
volumes :
postgres_data :
redis_data :
# Start all services
docker compose up -d
# View logs
docker compose logs -f web
# Scale service
docker compose up -d --scale web=3
# Stop all
docker compose down
# With volumes
docker compose down -v
# Enable permanently
export DOCKER_BUILDKIT=1
# Or per-build
DOCKER_BUILDKIT=1 docker build .
# Syntax directive for BuildKit
# syntax=docker/dockerfile:1
# --mount=type=cache for caching layers
FROM node:20
RUN --mount=type=cache,target=/root/.npm \
npm ci
# Build for multiple platforms
docker buildx build \
--platform linux/amd64,linux/arm64 \
--tag myapp:latest \
--push .
docker scout cves myapp:latest
docker scout recommendations myapp:latest
# Run with resource limits
docker run -d \
--name myapp \
--memory=512m \
--cpus=1.0 \
--restart=unless-stopped \
-e PYTHONUNBUFFERED=1 \
myapp:latest
# Healthcheck
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8000/health || exit 1