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
dockergroup, 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: 200Verify 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=secretso 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
/etcin a container that should be read-only. - ✓Access to
docker.sockor attempts to callmountorsetns. - ✓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.
- Remove
, privileged, host namespace sharing and docker.sock mounts everywhere. CI runners are the most frequent offenders. - Run as non-root, drop all capabilities, and enable no-new-privileges. Make these the defaults on single Docker hosts and production clusters.
- Patch the host kernel and Docker Engine, and restrict the docker group.
- Pin images by digest, scan in CI and sign images before they reach the registry.
- Add read-only filesystems, resource limits, network segmentation and file-mounted secrets.
- 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.
