Skip to main content

Docker Security:

 Docker Security: Best Practices and Image Scanning

📅 Published: Feb 2026
⏱️ Estimated Reading Time: 18 minutes
🏷️ Tags: Docker Security, Container Security, Image Scanning, DevSecOps, Container Hardening


Introduction: The Security Challenge

Containers share the host kernel. A vulnerability in a container can potentially affect other containers and the host itself. Unlike virtual machines, which have strong isolation boundaries, containers rely on proper configuration and security practices.

Docker security is not automatic. You must actively secure your images, containers, and the Docker daemon. This guide covers the essential security practices you need to protect your containerized applications.


Part 1: The Shared Responsibility Model

Who Is Responsible for What

ComponentResponsibility
Docker DaemonDocker and host administrator
Host OSHost administrator
Base ImagesImage maintainer and you
Application CodeYou
Runtime ConfigurationYou
Network SecurityYou
Secrets ManagementYou

The Docker platform provides isolation features. But you must configure and use them correctly.


Part 2: Image Security Best Practices

Use Official Images

Always prefer official images from Docker Hub. Official images are maintained by Docker or trusted partners. They receive security updates and follow best practices.

dockerfile
# Good: Official image
FROM node:18-alpine

# Risky: Unknown user image
FROM someuser/node:latest

Use Specific Tags, Never Latest

The latest tag is a moving target. It can change unexpectedly, introducing breaking changes or vulnerabilities.

dockerfile
# Bad: Unpredictable
FROM node:latest

# Good: Specific version
FROM node:18.17.0-alpine

# Good: Minor version with security patches
FROM node:18-alpine

Minimize Base Image Size

Smaller images have fewer vulnerabilities. Alpine Linux is a popular choice for minimal images.

Base ImageSizePackagesUse Case
alpine5 MBBusyBoxMinimal, security-focused
slim50-100 MBMinimal DebianBalance size and compatibility
full200-500 MBFull OSCompatibility, development
dockerfile
# Minimal Node.js image
FROM node:18-alpine  # ~170 MB

# Larger alternative
FROM node:18-slim     # ~200 MB
FROM node:18          # ~1 GB

Run as Non-Root

Running containers as root is dangerous. If an attacker escapes the container, they gain root access on the host.

dockerfile
# Bad: Running as root
FROM node:18
COPY . /app
CMD ["node", "app.js"]

# Good: Create and use non-root user
FROM node:18-alpine
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nodejs -u 1001 -G nodejs
USER nodejs
WORKDIR /app
COPY --chown=nodejs:nodejs . /app
CMD ["node", "app.js"]

Use Multi-Stage Builds

Multi-stage builds keep final images clean by excluding build tools and intermediate files.

dockerfile
# Build stage
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

# Final stage - no build tools
FROM node:18-alpine
RUN addgroup -S nodejs && adduser -S nodejs -G nodejs
USER nodejs
WORKDIR /app
COPY --from=builder --chown=nodejs:nodejs /app/node_modules ./node_modules
COPY --chown=nodejs:nodejs . .
CMD ["node", "app.js"]

Layer Security

  • Combine RUN commands to reduce layers

  • Clean up package manager cache in the same layer

  • Remove temporary files

dockerfile
# Bad: Multiple layers, leaves cache
RUN apt-get update
RUN apt-get install -y package
RUN apt-get clean

# Good: Single layer, cache removed
RUN apt-get update && \
    apt-get install -y package && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

Scan Images for Vulnerabilities

Never trust an image without scanning it.

bash
# Docker Scout (built-in)
docker scout quickview nginx:latest
docker scout cves nginx:latest

# Trivy
trivy image nginx:latest

# Grype
grype nginx:latest

Part 3: Runtime Security

Drop Unnecessary Capabilities

Linux capabilities give containers privileged access. Drop all capabilities, then add only what you need.

bash
# Run with minimal capabilities
docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE nginx

Common capabilities:

CapabilityPurposeWhen to Add
NET_BIND_SERVICEBind to ports below 1024Web servers
CHOWNChange file ownershipFile operations
DAC_OVERRIDEBypass file permissionsConfiguration
SETUID/SETGIDChange user/groupAuthentication

Use Seccomp Profiles

Seccomp (secure computing mode) restricts system calls a container can make.

bash
# Default seccomp profile (recommended)
docker run --security-opt seccomp=default.json nginx

# Disable seccomp (not recommended)
docker run --security-opt seccomp=unconfined nginx

Use AppArmor or SELinux

AppArmor (Ubuntu/Debian) and SELinux (CentOS/RHEL) provide mandatory access control.

bash
# AppArmor profile
docker run --security-opt apparmor=myprofile nginx

# SELinux context
docker run --security-opt label=type:myapp_t nginx

Read-Only Root Filesystem

Make the container's root filesystem read-only. Writeable directories must be volumes.

bash
docker run --read-only -v /tmp nginx
dockerfile
# Dockerfile for read-only containers
FROM nginx:alpine
RUN mkdir -p /var/cache/nginx /var/run && \
    chown -R nginx:nginx /var/cache/nginx /var/run
VOLUME /var/cache/nginx
VOLUME /var/run

Set Resource Limits

Prevent denial-of-service attacks by limiting resources.

bash
docker run \
  --memory=512m \
  --memory-swap=1g \
  --cpus=1 \
  --pids-limit=100 \
  nginx

Use User Namespaces

User namespaces map the container's root user to a non-root user on the host.

bash
# Enable in daemon.json
{
  "userns-remap": "default"
}

Part 4: Secrets Management

Never Embed Secrets in Images

Secrets in images are exposed forever. Anyone with image access can extract them.

dockerfile
# NEVER DO THIS
ENV DB_PASSWORD=secret123
COPY config/with-secret.json /app/config.json

Use Docker Secrets (Swarm)

Docker Swarm provides built-in secrets management.

bash
# Create secret
echo "secret123" | docker secret create db_password -

# Use in service
docker service create \
  --secret db_password \
  --name db \
  postgres

Use Environment Variables with External Management

bash
# Pass secret at runtime
docker run -e DB_PASSWORD=$(aws secretsmanager get-secret-value ...) postgres

# Use secret file
docker run --env-file=/run/secrets/db.env postgres

Use External Secrets Managers

For production, use dedicated secrets management:

  • HashiCorp Vault

  • AWS Secrets Manager

  • Azure Key Vault

  • Google Secret Manager


Part 5: Network Security

Use Custom Networks

Default bridge network allows containers to communicate. Use custom networks for isolation.

bash
# Create isolated network
docker network create --internal internal-network

# Run database on internal network (no external access)
docker run --network internal-network --name db postgres

# Run app on same network
docker run --network internal-network --name app myapp

Limit Container to Container Communication

Control which containers can communicate.

bash
# Create network with internal isolation
docker network create --internal isolated

# Only containers on this network can talk to each other

Avoid Host Network

Host network removes network isolation. Use only when necessary.

bash
# Avoid
docker run --network host nginx

# Prefer port mapping
docker run -p 80:80 nginx

Part 6: Docker Daemon Security

Use TLS for Remote Access

If you expose the Docker daemon remotely, use TLS.

bash
# Generate certificates
openssl genrsa -aes256 -out ca-key.pem 4096
openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -out ca.pem

# Configure daemon
{
  "tls": true,
  "tlsverify": true,
  "tlscacert": "/certs/ca.pem",
  "tlscert": "/certs/server-cert.pem",
  "tlskey": "/certs/server-key.pem",
  "hosts": ["tcp://0.0.0.0:2376"]
}

Restrict Access to Docker Socket

The Docker socket (/var/run/docker.sock) gives root access to the host. Never expose it unnecessarily.

bash
# Dangerous: Mounting docker socket
docker run -v /var/run/docker.sock:/var/run/docker.sock ...

# Better: Use Docker-in-Docker (dind) for CI

Audit Docker Daemon Logs

Monitor Docker daemon logs for suspicious activity.

bash
# View daemon logs
journalctl -u docker.service

# Monitor in real-time
journalctl -u docker.service -f

Part 7: Image Scanning Tools

Docker Scout

Docker Scout is built into Docker Desktop and Docker Hub.

bash
# Quick vulnerability overview
docker scout quickview nginx:latest

# Detailed CVE report
docker scout cves nginx:latest

# Compare images
docker scout compare nginx:latest nginx:1.24

# Generate SBOM
docker scout sbom nginx:latest

Trivy

Trivy is an open-source, comprehensive scanner.

bash
# Install
brew install aquasecurity/trivy/trivy

# Scan image
trivy image nginx:latest

# Scan with severity filter
trivy image --severity HIGH,CRITICAL nginx:latest

# Scan in CI
trivy image --exit-code 1 --severity CRITICAL myapp:latest

Grype

Grype focuses on vulnerability scanning.

bash
# Install
brew install anchore/grype/grype

# Scan image
grype nginx:latest

# Output formats
grype nginx:latest -o json > scan.json

Snyk Container

Snyk integrates with Docker and CI/CD pipelines.

bash
# Scan local image
snyk container test nginx:latest

# Monitor for new vulnerabilities
snyk container monitor nginx:latest

# Docker Desktop integration
docker scan nginx:latest

Part 8: Security Checklist

Image Security

  • Use official images

  • Pin specific versions (no latest)

  • Use minimal base images (alpine, slim)

  • Run as non-root user

  • Use multi-stage builds

  • Scan images before deployment

  • Remove unnecessary packages

  • Set filesystem permissions correctly

Runtime Security

  • Drop all capabilities, add only needed

  • Use read-only root filesystem

  • Set resource limits (memory, CPU, PIDs)

  • Use seccomp profiles

  • Use AppArmor or SELinux

  • Run with --security-opt no-new-privileges

Network Security

  • Use custom, user-defined networks

  • Avoid --network host

  • Use internal networks for backend services

  • Restrict container-to-container communication

  • Use firewall rules for external access

Secrets Management

  • No secrets in images

  • No secrets in environment variables in code

  • Use secrets managers

  • Rotate secrets regularly

  • Audit secret access

Host Security

  • Keep Docker Engine updated

  • Use TLS for remote access

  • Restrict Docker socket access

  • Enable user namespace remapping

  • Audit daemon logs


Part 9: Real-World Security Scenarios

Scenario 1: Production Web Application

bash
# Secure run command for web application
docker run -d \
  --name webapp \
  --user 1000:1000 \
  --read-only \
  --tmpfs /tmp:rw,noexec,nosuid,size=100m \
  --tmpfs /run:rw,noexec,nosuid,size=50m \
  --cap-drop=ALL \
  --cap-add=NET_BIND_SERVICE \
  --security-opt no-new-privileges \
  --memory=512m \
  --cpus=0.5 \
  --pids-limit=100 \
  -p 80:80 \
  -v uploads:/app/uploads \
  mywebapp:latest

Scenario 2: CI/CD Pipeline with Scanning

yaml
# GitHub Actions workflow
name: Security Scan

on: push

jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Build image
        run: docker build -t myapp:${{ github.sha }} .
      
      - name: Scan with Trivy
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: myapp:${{ github.sha }}
          format: 'sarif'
          output: 'trivy-results.sarif'
          severity: 'CRITICAL,HIGH'
      
      - name: Upload results
        uses: github/codeql-action/upload-sarif@v2
        with:
          sarif_file: 'trivy-results.sarif'

Scenario 3: Isolated Development Environment

yaml
# docker-compose.yml
version: '3.8'

services:
  app:
    build: .
    user: 1000:1000
    read_only: true
    tmpfs:
      - /tmp:noexec,nosuid,size=100m
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE
    security_opt:
      - no-new-privileges
    environment:
      - NODE_ENV=development
    volumes:
      - .:/app:ro
      - node_modules:/app/node_modules
    ports:
      - "3000:3000"

volumes:
  node_modules:

Summary

CategoryKey PracticeTool/Command
ImagesUse official, pin versions, minimal baseFROM alpine
RuntimeNon-root, read-only, drop caps--user--read-only--cap-drop
NetworkCustom networks, no host mode--network
SecretsExternal managementDocker secrets, Vault
ScanningRegular vulnerability checksTrivy, Grype, Docker Scout
DaemonTLS, user namespace/etc/docker/daemon.json

Container security is layered. No single measure is sufficient. Combine multiple practices for defense in depth.


Practice Questions

  1. Why should you avoid running containers as root?

  2. What is the purpose of multi-stage builds in security?

  3. What are the risks of using the latest tag?

  4. How do you prevent a container from writing to its root filesystem?

  5. What command scans a Docker image for vulnerabilities?


Learn More

Practice Docker security with hands-on exercises in our interactive labs:
https://devops.trainwithsky.com/

Comments

Popular posts from this blog

Introduction to Terraform – The Future of Infrastructure as Code

  Introduction to Terraform – The Future of Infrastructure as Code In today’s fast-paced DevOps world, managing infrastructure manually is outdated . This is where Terraform comes in—a powerful Infrastructure as Code (IaC) tool that allows you to define, provision, and manage cloud infrastructure efficiently . Whether you're working with AWS, Azure, Google Cloud, or on-premises servers , Terraform provides a declarative, automation-first approach to infrastructure deployment. Shape Your Future with AI & Infinite Knowledge...!! Read In-Depth Tech & Self-Improvement Blogs http://www.skyinfinitetech.com Watch Life-Changing Videos on YouTube https://www.youtube.com/@SkyInfinite-Learning Transform Your Skills, Business & Productivity – Join Us Today! In today’s digital-first world, agility and automation are no longer optional—they’re essential. Companies across the globe are rapidly shifting their operations to the cloud to keep up with the pace of innovatio...

📊 Monitoring & Logging in Kubernetes – Tools like Prometheus, Grafana, and Fluentd

  Monitoring & Logging in Kubernetes – Tools like Prometheus, Grafana, and Fluentd Monitoring and logging are essential for maintaining a healthy and well-performing Kubernetes cluster. In this guide, we’ll cover why monitoring is important, key monitoring tools like Prometheus and Grafana, and logging tools like Fluentd to help you gain visibility into your cluster’s performance and logs. Shape Your Future with AI & Infinite Knowledge...!! Want to Generate Text-to-Voice, Images & Videos? http://www.ai.skyinfinitetech.com Read In-Depth Tech & Self-Improvement Blogs http://www.skyinfinitetech.com Watch Life-Changing Videos on YouTube https://www.youtube.com/@SkyInfinite-Learning Transform Your Skills, Business & Productivity – Join Us Today! 🚀 Introduction In today’s fast-paced cloud-native environment, Kubernetes has emerged as the de-facto container orchestration platform. But deploying and managing applications in Kubernetes is just half the ba...

🔒 Kubernetes Security – RBAC, Network Policies, and Secrets Management

  Kubernetes Security – RBAC, Network Policies, and Secrets Management Security is a critical aspect of managing Kubernetes clusters. In this guide, we'll cover essential security mechanisms like Role-Based Access Control (RBAC) , Network Policies , and Secrets Management to help you secure your Kubernetes environment effectively. Shape Your Future with AI & Infinite Knowledge...!! Want to Generate Text-to-Voice, Images & Videos? http://www.ai.skyinfinitetech.com Read In-Depth Tech & Self-Improvement Blogs http://www.skyinfinitetech.com Watch Life-Changing Videos on YouTube https://www.youtube.com/@SkyInfinite-Learning Transform Your Skills, Business & Productivity – Join Us Today! 🚀 Introduction: Why Kubernetes Security Is Non-Negotiable As Kubernetes becomes the backbone of modern cloud-native infrastructure, security is no longer optional—it’s mission-critical . With multiple moving parts like containers, pods, services, nodes, and more, Kuberne...