Docker container security: 15 best practices from image to runtime

Docker container security: 15 best practices from image to runtime

Santerra Holler October 06, 2026

Docker container security: 15 best practices from image to runtime

Docker container security comes down to 15 practices across the container lifecycle. Build minimal, signed images; protect the host and Docker daemon; run every container with least privilege; isolate networks and secrets; and monitor what executes at runtime. Most container breaches follow a few predictable paths: privileged containers, a mounted Docker socket, unpatched images, or secrets baked into layers. This guide groups each practice by lifecycle stage and pairs it with commands, Compose snippets and verification steps. Each one is a control you can apply and confirm today, whether you run a single Docker host or a production Kubernetes cluster.

Key takeaways

  • ✓The highest-impact Docker controls are never using, privileged, never mounting /var/run/docker.sock, and never running containers as root.
  • ✓Pinning base images by digest and scanning them in CI stops most known vulnerabilities before they reach production.
  • ✓Dropping all Linux capabilities and enabling no-new-privileges blocks the most common privilege escalation paths inside a container.
  • ✓Environment variables are a weak place for secrets because docker inspect, /proc and application logs can expose them.
  • ✓Image scanning finds what could be exploited, while runtime monitoring shows what is actually loaded, reachable and behaving suspiciously.

Docker security best practices at a glance

The 15 Docker security best practices below cover build time, the host, runtime, and operations, in roughly the order a container moves through its life.

# Practice Stage How to verify
1 Use minimal, trusted base images Build Image size and package count in the SBOM
2 Pin images by digest and rebuild on a schedule Build FROM lines contain @sha256:
3 Scan images and gate CI on findings CI/CD Pipeline fails on critical CVEs
4 Sign images and verify provenance Registry cosign verify or Docker Content Trust succeeds
5 Set a non-root USER Build docker exec id returns a non-zero UID
6 Patch the host kernel and Docker Engine Host uname -r and docker version are current
7 Protect the daemon and socket Host No container mounts docker.sock
8 No privileged mode; drop all capabilities Runtime HostConfig.Privileged is false
9 Enable no-new-privileges Runtime NoNewPrivs: 1 in /proc/1/status
10 Apply seccomp, AppArmor/SELinux and user namespaces Runtime docker inspect shows SecurityOpt
11 Read-only root filesystem Runtime Writes outside tmpfs fail
12 Set CPU, memory and PID limits Runtime docker stats shows limits
13 Segment networks and minimize ports Network docker port lists only intended ports
14 Keep secrets out of images and env vars Data docker inspect shows no credentials
15 Monitor runtime behavior and audit continuously Operations Alerts fire on shell spawns and socket access

Common Docker container security vulnerabilities

The most common Docker container security vulnerabilities are excessive privilege, an exposed Docker socket, unpatched images, and leaked secrets. Each one gives an attacker a realistic path in.

Insecure default Why it is dangerous Secure alternative
, privileged Grants all capabilities and host device access, so breakout is trivial , cap-drop ALL, add back only what is needed
-v /var/run/docker.sock:/var/run/docker.sock Socket access lets an attacker start a privileged container, which equals root on the host Rootless Docker, a filtered socket proxy, or Kaniko/BuildKit for builds
, pid=host or, network=host Exposes host processes and interfaces, so an attacker can inject into processes and sniff network traffic Default namespaces plus user-defined networks
Running as root (UID 0) A kernel or runtime bug becomes host root USER 10001 in the Dockerfile, or rootless mode
Unpinned :latest base image Silent upstream changes or a poisoned image enter the build FROM image@sha256:… with signature checks
Secrets in ENV or image layers Anyone who pulls the image or runs docker inspect can read them Mounted secrets or an external secret manager

Vulnerability data has gaps too. Some CVEs lack CPE applicability statements or CVSS scores in the NVD, so scanners that match only on NVD data miss findings. Use scanners with multisource advisory feeds and package-based matching, and define fallback remediation rules for CVEs without a score.

Build-time: secure images and the supply chain

Ship the smallest verified, non-root image you can. Fewer packages and fewer privileges leave an attacker less to exploit.

1–2. Minimal base images, pinned by digest

Use official or well-maintained bases, prefer distroless or slim variants, and use multi-stage builds so compilers and shells never reach production. Pin by digest so a tag change cannot alter your build. Rebuild on a schedule, for example weekly, to pick up base image patches.

# Build stage
FROM golang:1.23@sha256:<digest> AS build
WORKDIR /src
COPY . .
RUN CGO_ENABLED=0 go build -o /app ./cmd/api

# Runtime stage: no shell, no package manager
FROM gcr.io/distroless/static-debian12@sha256:<digest>
COPY --from=build /app /app
# practice 5: non-root
USER 10001:10001
ENTRYPOINT ["/app"]

3. Scan images and gate CI

Scan in the pipeline and fail the build on critical findings, for example with trivy image --exit-code 1 --severity CRITICAL,HIGH myapp:1.4.2. Enforce the gate with your CI/CD security tools instead of trusting developers to run scans locally.

4. Sign images and verify provenance

Sign every image you push and verify signatures before deployment. Set DOCKER_CONTENT_TRUST=1 to make Docker reject unsigned images, or use Sigstore’s cosign verify. Generate an SBOM and provenance attestations. Where vendors publish VEX documents, use them to suppress findings that cannot be exploited.

5. Set a non-root user

A non-root USER limits the damage from an application compromise. Rootless Docker goes further and runs the daemon itself without root. Use both in production.

Host, daemon and runtime hardening

Runtime hardening limits what a compromised container can do. It depends on a patched host, because all containers share the host kernel.

6–7. Patch the host and protect the daemon

  • ✓Patch the host kernel and Docker Engine on a fixed cadence, because a kernel CVE is a container escape CVE.
  • ✓Restrict membership of the docker group, since it is equivalent to root.
  • ✓Never expose the daemon on TCP 2375 without TLS, and never mount the socket into a workload.
  • ✓Set daemon defaults in /etc/docker/daemon.json: {"icc": false, "userns-remap": "default", "no-new-privileges": true}.

8–12. Least privilege and confinement

docker run -d --name api \
  --user 10001:10001 \
  --cap-drop ALL --cap-add NET_BIND_SERVICE \
  --security-opt no-new-privileges:true \
  --security-opt seccomp=default.json \
  --read-only --tmpfs /tmp:rw,noexec,nosuid,size=64m \
  --memory 512m --cpus 1 --pids-limit 200 \
  myapp@sha256:<digest>

Each of these controls blocks something different. Capability dropping removes kernel privileges. Seccomp filters syscalls, and Docker’s default profile blocks around 40+ risky calls, including mount and kexec_load. AppArmor or SELinux restricts file and resource access, and user namespaces map container root to an unprivileged host UID. Run all of them together, because each one closes paths the others leave open. In Kubernetes, the same settings map to pod securityContext fields (runAsNonRoot, allowPrivilegeEscalation: false, readOnlyRootFilesystem) and Pod Security Admission. See these Kubernetes security best practices for the cluster-level equivalents.

Using no-new-privileges in Docker Compose

Enable no-new-privileges in Compose with security_opt. It stops processes from gaining privileges through setuid or setgid binaries:

services:
  api:
    image: myapp@sha256:<digest>
    user: "10001:10001"
    read_only: true
    cap_drop: [ALL]
    security_opt:
      - no-new-privileges:true
    tmpfs: [/tmp]
    pids_limit: 200

Verify with docker exec api grep NoNewPrivs /proc/1/status, which should return NoNewPrivs: 1. Run Docker 18.09.7 or newer, because older versions let docker exec bypass this protection.

Network, secrets and runtime monitoring

The last three practices limit lateral movement, protect credentials, and catch attacks that get past build-time controls.

13. Segment networks

Put each application tier on its own user-defined network. Publish only required ports and bind internal services to 127.0.0.1. Disable inter-container communication on the default bridge with icc: false. Use TLS or mTLS between services, and restrict egress so a compromised container cannot reach arbitrary IPs.

14. Manage secrets outside images and env vars

  • ✓Build-time: use BuildKit RUN --mount=type=secret so secrets never land in a layer.
  • ✓Runtime: use Docker secrets, which are mounted as files under /run/secrets/, instead of ENV.
  • ✓Production: pull short-lived credentials from HashiCorp Vault or AWS Secrets Manager and rotate them automatically.

15. Monitor and audit continuously

Audit configuration with Docker Bench for Security against the CIS Docker Benchmark, and add auditd rules on dockerd and /var/lib/docker. At runtime, alert on these indicators:

  • ✓Shells (sh, bash) spawned in containers that have no reason to run them.
  • ✓Writes to binary directories or /etc in a container that should be read-only.
  • ✓Access to docker.sock or attempts to call mount or setns.
  • ✓Outbound connections to unknown IPs or mining pools.

Feed scanner findings into risk-based vulnerability management so teams fix exploitable, running packages first instead of working through the backlog by CVSS score alone.

How Upwind adds runtime context to Docker security

Upwind secures containers with lightweight eBPF sensors that show what is running, so teams can rank Docker risk by real runtime exposure instead of by static scan results. Upwind holds a 4.8/5 rating from 88 reviews on Gartner Peer Insights as of October 2026. Teams that run Docker only on developer laptops or a single non-production host may need less than a full CNAPP.

  • ✓Shows which image vulnerabilities are loaded and reachable at runtime.
  • ✓Detects threats as they happen inside containers and Kubernetes workloads.
  • ✓Covers posture, workload protection, identities and APIs from one sensor.
  • ✓Uses AI agents (the Agentic Pack) to investigate threats, validate exposure and generate fixes.

Where to start: a prioritized hardening order

Start with the controls that block host takeover, then add supply-chain and detection layers. The order below matches each step to the environments where it matters most.

  1. Remove , privileged, host namespace sharing and docker.sock mounts everywhere. CI runners are the most frequent offenders.
  2. Run as non-root, drop all capabilities, and enable no-new-privileges. Make these the defaults on single Docker hosts and production clusters.
  3. Patch the host kernel and Docker Engine, and restrict the docker group.
  4. Pin images by digest, scan in CI and sign images before they reach the registry.
  5. Add read-only filesystems, resource limits, network segmentation and file-mounted secrets.
  6. Turn on runtime monitoring and schedule a CIS benchmark audit every quarter.

Applied in this order, these 15 practices turn docker container security from a long list of warnings into controls you can check, starting at the first FROM line and ending at the last running process.

FAQ

What are the most common Docker container security risks?

The article highlights four common risks: privileged containers, mounted Docker sockets, unpatched images, and secrets baked into image layers or exposed through environment variables.

Why should Docker images be pinned by digest instead of using tags like latest?

Pinning by digest prevents silent upstream tag changes from altering your build. It gives you an exact image version to deploy and verify, especially when combined with image signing and scheduled rebuilds for patches.

Why is running containers as non-root so important?

Running as a non-root user limits the damage if an application is compromised. The guide recommends setting a non-root USER in the Dockerfile and combining it with dropped Linux capabilities and no-new-privileges to reduce escalation paths.

Are environment variables a safe place to store Docker secrets?

No. The article explains that environment variables are weak for secrets because docker inspect, procfs paths, and application logs can expose them. Safer options include Docker secrets, BuildKit secret mounts, or external secret managers like Vault or AWS Secrets Manager.

What is the difference between image scanning and runtime monitoring?

Image scanning finds known vulnerabilities before deployment, while runtime monitoring shows what is actually loaded, reachable, and behaving suspiciously in running containers. The article recommends using both because static scans and runtime telemetry answer different security questions.

Contents
Add the Upwind RSS Feed to Slack
Connect the Upwind RSS Feed to your Slack.
Follow the how-to here.
Threat RSS
Add the Upwind RSS Feed to Slack
Connect the Upwind RSS Feed to your Slack.
Follow the how-to here.
Main RSS