Skip to main content

Free 30-min security demo  — We'll scan your real code and show live findings, no commitment Book Now

Offensive360
DevSecOps

How to Secure Docker Containers — Security Guide

Docker containers offer enhanced isolation, but organizations must implement specific safeguards. This guide covers image scanning, least privilege, network policies, secrets management, and runtime monitoring.

Offensive360 Security Research Team — min read
Docker containers DevSecOps image scanning least privilege

Docker containers have transformed how organizations build and deploy applications. They offer faster startup times than VMs, consistent environments, and strong process isolation. But isolation is not the same as security — containers share the host kernel, and a misconfigured container can expose your entire infrastructure.

1. Run Containers as Non-Root

Docker doesn’t require containers to run as root, and almost no production workload should. Running as root means that if the application is compromised, the attacker has root access inside the container and potential kernel-level access on the host.

# WRONG: Default is root
FROM node:18

# CORRECT: Create and switch to a non-root user
FROM node:18-alpine
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
WORKDIR /app
COPY --chown=appuser:appgroup . .

Set runAsNonRoot: true in Kubernetes pod specs to enforce this at the orchestration layer.

2. Implement Resource Quotas

Without resource limits, a single misbehaving container can starve the entire host of CPU and memory — whether through a bug or a deliberate attack.

# docker-compose.yml
services:
  api:
    image: myapp:latest
    deploy:
      resources:
        limits:
          cpus: '0.5'
          memory: 512M
        reservations:
          cpus: '0.25'
          memory: 256M

In Kubernetes:

resources:
  limits:
    cpu: "500m"
    memory: "512Mi"
  requests:
    cpu: "250m"
    memory: "256Mi"

3. Use Trusted Registries

Pull images only from trusted registries. Public Docker Hub images are not vetted for security — many popular images contain outdated software, unpatched CVEs, or in some cases, deliberately malicious code.

Best practices:

  • Use official base images (tagged official on Docker Hub)
  • Mirror approved images to a private registry (Docker Trusted Registry, AWS ECR, GCR, GitHub Container Registry)
  • Implement registry access controls — not everyone should be able to push images
  • Block unapproved public registries at the network level

4. Scan Images for Vulnerabilities

Every Docker image is built from a base image plus layers of installed packages. Each layer can contain CVEs. Scan images before pushing to your registry and before deploying to production.

# Using Trivy (open source)
trivy image myapp:latest

# Using Docker Scout
docker scout cves myapp:latest

Integrate scanning into your CI pipeline so vulnerable images are blocked before merge.

5. Keep Images Minimal

Smaller images have a smaller attack surface. Use multi-stage builds to produce minimal final images:

# Build stage
FROM golang:1.21 AS builder
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 go build -o server .

# Final stage — minimal image
FROM scratch
COPY --from=builder /app/server /server
ENTRYPOINT ["/server"]

Avoid installing debugging tools, shells, or package managers in production images. If you need to debug, do it in a separate debugging sidecar.

6. Never Store Secrets in Images

Secrets embedded in Docker images — even in intermediate build layers — are extractable by anyone with access to the image.

# This exposes the secret in the image layer history
RUN apt-get install -y curl && \
    curl -H "Authorization: Bearer $SECRET_TOKEN" https://internal-api/...

Use Docker Buildx secrets for build-time secrets:

docker buildx build --secret id=mysecret,src=./secret.txt .

For runtime secrets, use Docker secrets, Kubernetes Secrets, or a dedicated secrets manager (HashiCorp Vault, AWS Secrets Manager).

7. Apply Read-Only Filesystems

Make the container filesystem read-only to prevent malicious code from writing to disk:

# docker run
docker run --read-only myapp:latest

# Kubernetes
securityContext:
  readOnlyRootFilesystem: true

If the application needs to write files, mount specific writable volumes rather than making the entire filesystem writable.

8. Network Segmentation

Containers on the same Docker network can communicate freely by default. Segment your networks:

# docker-compose.yml
networks:
  frontend:
  backend:

services:
  nginx:
    networks: [frontend, backend]
  api:
    networks: [backend]
  db:
    networks: [backend]

The database is only reachable from the backend network — not from the public-facing nginx container.

9. Monitor API and Network Activity

Containers rely on the Docker API and network activity for inter-process communication. Monitor for anomalies:

  • Unexpected outbound connections
  • Unusual process execution (shells launched inside a container)
  • Filesystem writes in unexpected locations
  • Network port scanning behavior

Tools like Falco provide runtime security monitoring for containerized environments.

10. Scan Your Application Code

Docker images contain your application code. Vulnerabilities in the code itself — SQL injection, hardcoded credentials, insecure cryptography — exist regardless of how well the container is hardened. Run SAST against your source code as part of the image build pipeline.

Summary

Docker security requires a layered approach:

LayerControl
ImageScan for CVEs, use minimal base images, non-root user
BuildNo secrets in layers, multi-stage builds
RuntimeRead-only filesystem, resource limits, network segmentation
OperationsRegistry access controls, runtime monitoring, secret management

No single control is sufficient. Implement all layers for defense in depth.

Offensive360 Security Research Team

Application Security Research

Find vulnerabilities before attackers do

Run Offensive360 SAST and DAST against your applications and get a full vulnerability report in minutes.