DockerContainersIsolation5 min read

Docker container security

Non-root containers, read-only filesystems, resource limits, network isolation and image scanning.


Docker security principles

Docker is not secure by default. Follow these practices to reduce risks:

  • Never run containers as root
  • Don't use --privileged unless absolutely necessary
  • Limit resources (CPU, RAM)
  • Isolate networks between containers
  • Scan images before using them

Non-root containers

In your Dockerfile:

dockerfile
FROM node:20-alpine

# Create non-root user
RUN addgroup -S appgroup && adduser -S appuser -G appgroup

WORKDIR /app
COPY --chown=appuser:appgroup . .

# Switch to non-root user
USER appuser

CMD ["node", "server.js"]

Read-only filesystem

Run containers with read-only filesystem:

bash
docker run --read-only --tmpfs /tmp --tmpfs /var/run my-app

In docker-compose:

yaml
services:
  app:
    image: my-app
    read_only: true
    tmpfs:
      - /tmp
      - /var/run

Resource limits

yaml
services:
  app:
    image: my-app
    deploy:
      resources:
        limits:
          cpus: '1.0'
          memory: 512M
        reservations:
          cpus: '0.25'
          memory: 128M

Or with docker run:

bash
docker run --memory=512m --cpus=1.0 my-app

Network isolation

Create separate networks for each service group:

yaml
services:
  app:
    networks:
      - frontend
      - backend
  db:
    networks:
      - backend
  nginx:
    networks:
      - frontend

networks:
  frontend:
  backend:
    internal: true  # No internet access

The database is only accessible from the backend network.

Image scanning with Trivy

bash
# Install Trivy
sudo apt install -y wget
wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | sudo apt-key add -
echo 'deb https://aquasecurity.github.io/trivy-repo/deb generic main' | sudo tee /etc/apt/sources.list.d/trivy.list
sudo apt update && sudo apt install -y trivy

# Scan an image
trivy image my-app:latest

# Scan only critical vulnerabilities
trivy image --severity CRITICAL my-app:latest

Integrate Trivy in your CI/CD to scan before deploying.

Docker Secrets

Never put passwords in Dockerfile environment variables:

yaml
services:
  app:
    secrets:
      - db_password

secrets:
  db_password:
    file: ./secrets/db_password.txt

In the app, read the secret from /run/secrets/db_password.

Additional best practices

dockerfile
# Use minimal images (alpine)
FROM node:20-alpine

# Don't install unnecessary packages
RUN apk add --no-cache dumb-init

# Use .dockerignore
# Copy only what's needed
COPY package.json package-lock.json ./
RUN npm ci --production
COPY src/ ./src/

Recommendations

  • Use official and minimal images (alpine)
  • Update base images regularly
  • Don't expose the Docker socket (/var/run/docker.sock)
  • Use Docker Bench Security to audit your installation
  • Implement health checks in all containers
  • Never store secrets in the image

Was this guide helpful?