π³ Docker Containers & Images
Docker is a platform for building, shipping, and running applications in lightweight, isolated containers. Unlike virtual machines, containers share the host OS kernel, making them fast to start and efficient in resource usage.
Core Concepts
- Image β A read-only, layered template built from a Dockerfile. Images are stored in registries.
- Container β A running instance of an image. Containers are ephemeral unless volumes are used.
- Layer β Each Dockerfile instruction creates an immutable layer. Layers are cached and shared between images.
- Registry β A repository for images (Docker Hub, AWS ECR, GitHub Container Registry).
- Volume β A persistent data store that survives container restarts and removals.
Writing Dockerfiles
A Dockerfile is a text file with instructions that Docker uses to build an image. Each instruction adds a new layer to the image.
Layer Caching Order instructions from least to most frequently changed. Put COPY package.json and RUN npm install before COPY . . to maximize cache reuse.
# Dockerfile β Node.js API (optimized for caching)
FROM node:20-alpine AS base
WORKDIR /app
# Install dependencies first (cached until package.json changes)
COPY package.json package-lock.json ./
RUN npm ci --only=production && npm cache clean --force
# Copy source code
COPY src/ ./src/
COPY config/ ./config/
# Create non-root user for security
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
# Runtime configuration
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=10s --start-period=15s --retries=3 \
CMD wget -qO- http://localhost:8080/health || exit 1
CMD ["node", "src/index.js"]
Multi-Stage Builds
Multi-stage builds drastically reduce final image size by using separate build and runtime stages. Build tools, compilers, and test dependencies stay in the build stage and never reach production.
# Multi-stage build β Go application
# Stage 1: Build
FROM golang:1.22-alpine AS builder
WORKDIR /build
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build \
-ldflags="-w -s" \
-o /app/server ./cmd/server
# Stage 2: Production image (~12MB vs ~350MB)
FROM scratch
COPY --from=builder /app/server /server
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
EXPOSE 8080
ENTRYPOINT ["/server"]
Networking
Docker provides three built-in network drivers:
- bridge β Default. Isolated network on the host. Containers can communicate by name.
- host β Container shares the host's network stack. Maximum performance, no isolation.
- none β No networking. Used for batch jobs that don't need network access.
# Create an isolated network
docker network create --driver bridge app-network
# Run containers on the same network (they resolve by name)
docker run -d --name postgres --network app-network postgres:16-alpine
docker run -d --name api --network app-network \
-e DB_HOST=postgres \
-p 8080:8080 \
registry.maxiscomputers.com/api:2.1.0
# Inspect network
docker network inspect app-network
Docker Compose
Docker Compose defines and runs multi-container applications using a YAML file. Ideal for local development and integration testing.
# docker-compose.yml
services:
api:
build:
context: .
dockerfile: Dockerfile
target: builder
ports:
- "8080:8080"
environment:
- NODE_ENV=development
- DB_URL=postgresql://user:pass@postgres:5432/appdb
- REDIS_URL=redis://redis:6379
volumes:
- ./src:/app/src:ro # hot reload in dev
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_started
restart: unless-stopped
postgres:
image: postgres:16-alpine
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: appdb
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user -d appdb"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
command: redis-server --appendonly yes
volumes:
- redis_data:/data
volumes:
postgres_data:
redis_data:
Container Security
Never run as root Always create a dedicated non-root user in your Dockerfile. Running as root inside a container is a critical security risk.
- Use distroless or Alpine base images to minimize the attack surface.
- Scan images with
docker scout,trivy, orgrypein your CI pipeline. - Use read-only filesystems where possible:
--read-onlyflag. - Never bake secrets into images. Use environment variables or secret mounts.
- Set
--cap-drop=ALLand add only required Linux capabilities. - Enable Docker Content Trust (
DOCKER_CONTENT_TRUST=1) for image signing verification.
Docker CLI Reference
# Build & Tag
docker build -t registry.maxiscomputers.com/api:2.1.0 --platform linux/amd64 .
docker tag registry.maxiscomputers.com/api:2.1.0 registry.maxiscomputers.com/api:latest
# Push & Pull
docker push registry.maxiscomputers.com/api:2.1.0
docker pull registry.maxiscomputers.com/api:2.1.0
# Run
docker run -d --name api -p 8080:8080 --restart unless-stopped \
-e NODE_ENV=production \
registry.maxiscomputers.com/api:2.1.0
# Inspect & Debug
docker ps -a
docker logs -f --tail 200 api
docker exec -it api sh
docker inspect api | jq '.[0].NetworkSettings'
docker stats # live resource usage
# Cleanup
docker system prune -af --volumes # remove ALL unused resources
docker image prune -f # remove dangling images only