Docker Security: Hardening Your Container Environment
Docker security best practices: non-root user, read-only filesystem, seccomp profiles, image scanning, Docker socket security, network isolation, and secrets management.
Table of Contents
Docker Security: Hardening Your Container Environment
Docker containers can harbor many security vulnerabilities in their default configuration. Containers running as root, over-privileged containers, and insecure images pose serious risks. In this guide, we'll cover Docker security layer by layer.
1. Running as Non-Root User
The most fundamental security measure: running containers as a non-root user.
Defining a User in Dockerfile
# Bad practice — running as root
FROM node:20
WORKDIR /app
COPY . .
RUN npm install
CMD ["node", "server.js"]
# Good practice — non-root user
FROM node:20
# Create user and group
RUN groupadd -r appgroup && useradd -r -g appgroup appuser
WORKDIR /app
COPY --chown=appuser:appgroup . .
RUN npm ci --only=production
# Switch to non-root user
USER appuser
CMD ["node", "server.js"]
Specifying User at Runtime
# Run with specific user
docker run -d --user 1000:1000 myapp:latest
# Run with current user
docker run -d --user $(id -u):$(id -g) myapp:latest
# Check user
docker exec CONTAINER_NAME whoami
docker exec CONTAINER_NAME id
2. Read-Only Filesystem
# Run with read-only root filesystem
docker run -d \
--name myapp \
--read-only \
--tmpfs /tmp \
--tmpfs /var/run \
myapp:latest
# Make specific directories writable
docker run -d \
--name myapp \
--read-only \
--tmpfs /tmp:rw,noexec,nosuid,size=100m \
-v app-logs:/app/logs \
myapp:latest
# docker-compose.yml
services:
app:
image: myapp:latest
read_only: true
tmpfs:
- /tmp
- /var/run
volumes:
- app-logs:/app/logs
3. Capability Management
Linux capabilities provide granular control over root privileges.
# Drop all capabilities, add only what's needed
docker run -d \
--name myapp \
--cap-drop ALL \
--cap-add NET_BIND_SERVICE \
myapp:latest
# NEVER use privileged mode (security risk)
# docker run --privileged myapp # BAD PRACTICE
# Check current capabilities
docker exec CONTAINER_NAME cat /proc/1/status | grep Cap
# docker-compose.yml
services:
app:
image: myapp:latest
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
- CHOWN
4. Seccomp Profiles
Seccomp restricts the system calls a container can make.
# Run with default seccomp profile (recommended)
docker run -d --security-opt seccomp=default myapp:latest
# Custom seccomp profile
docker run -d \
--security-opt seccomp=/path/to/seccomp-profile.json \
myapp:latest
# Disabling seccomp (security risk)
# docker run --security-opt seccomp=unconfined myapp # BAD PRACTICE
5. Docker Image Security
Secure Dockerfile Practices
# Minimal image with multi-stage build
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
# Production image — minimal
FROM node:20-alpine
# Apply security updates
RUN apk update && apk upgrade && apk add --no-cache dumb-init
# Non-root user
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app
COPY --from=builder --chown=appuser:appgroup /app/dist ./dist
COPY --from=builder --chown=appuser:appgroup /app/node_modules ./node_modules
USER appuser
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget -qO- http://localhost:3000/health || exit 1
ENTRYPOINT ["dumb-init", "--"]
CMD ["node", "dist/server.js"]
Image Scanning
# Scan with Trivy (recommended)
docker run --rm \
-v /var/run/docker.sock:/var/run/docker.sock \
aquasec/trivy:latest image myapp:latest
# Only critical and high severity
docker run --rm \
-v /var/run/docker.sock:/var/run/docker.sock \
aquasec/trivy:latest image \
--severity CRITICAL,HIGH \
myapp:latest
# Scan with Docker Scout
docker scout cves myapp:latest
docker scout recommendations myapp:latest
6. Docker Socket Security
# Restrict access to Docker socket
# Only add necessary users to docker group
usermod -aG docker appuser
# Mount Docker socket read-only (for monitoring)
docker run -d \
--name monitoring \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
monitoring-app:latest
# NEVER mount Docker socket in production containers
# This gives full access to the host!
7. Secrets Management
# Environment variable secret (insecure — visible via docker inspect)
docker run -e DB_PASSWORD=secret myapp # BAD PRACTICE
# Use Docker secrets (Swarm mode)
echo "StrongPass123!" | docker secret create db_password -
docker service create \
--name myapp \
--secret db_password \
myapp:latest
# Secret accessible at /run/secrets/db_password
# docker-compose.yml
services:
app:
image: myapp:latest
env_file:
- .env
secrets:
- db_password
secrets:
db_password:
file: ./secrets/db_password.txt
8. Network Security
# Restrict inter-container communication
docker network create \
--driver bridge \
--opt com.docker.network.bridge.enable_icc=false \
secure-network
# Only expose necessary ports
docker run -d \
--name myapp \
-p 127.0.0.1:8080:8080 \
myapp:latest
# 127.0.0.1 restricts access to localhost only
9. Resource Limits
# CPU and memory limits
docker run -d \
--name myapp \
--memory 512m \
--memory-swap 512m \
--cpus 1.0 \
--pids-limit 100 \
myapp:latest
10. Docker Daemon Security
// /etc/docker/daemon.json
{
"icc": false,
"no-new-privileges": true,
"userns-remap": "default",
"live-restore": true,
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
Conclusion
Docker security requires a multi-layered approach. Start with non-root user, read-only filesystem, minimal capabilities, and image scanning. Never pass secrets as environment variables, don't mount the Docker socket unnecessarily, and protect against DoS attacks with resource limits. With these security measures on REXE servers, you can keep your container infrastructure secure.
Related Articles
K3s Kubernetes Setup: Lightweight Container Orchestration
Single and multi-node Kubernetes cluster setup with K3s, kubectl usage, creating deployments and services, application management with Helm. Kubernetes guide for VPS environments.
Docker Volume and Network Management: Persistent Storage and Network Configuration
Docker volume types (named, bind mount, tmpfs), volume management, Docker network modes (bridge, host, overlay), creating custom networks and container communication. Comprehensive guide.
Container Logging: Docker and Kubernetes Log Management
Docker log drivers, docker logs command, Kubernetes pod logs, centralized log collection (Loki, ELK Stack), log rotation, and production log management best practices.