docker5 min readDocker Security Best Practices: 15 Critical Mistakes Teams Keep Making (and How to Fix Them)Learn the 15 most common Docker security mistakes that expose production containers to attacks, and follow these concrete fixes with code examples to secure your infrastructure today.ShieldOps AI2026-06-12 ·75Containers were supposed to make application deployment safer by design. Isolated processes, immutable images, and reproducible environments all sound like security wins. Yet in practice, Docker environments remain one of the most frequently exploited attack surfaces in modern infrastructure.The problem is not Docker itself. It is the gap between what teamsthinkthey are doing and what they areactuallydoing. In this guide, we cover the fifteen most common Docker security mistakes we see in production environments, and the specific practices that fix them. No generic advice. Just concrete steps you can implement today.The Real Cost of Getting Docker Security WrongBefore diving into the fixes, it is worth understanding why this matters. According to recent industry data, over 60% of organizations now run containerized workloads in production, but fewer than one in four have implemented a container-specific security framework. That gap translates directly to breach risk.When a container is compromised, the damage is rarely limited to that single container. Because containers share the host kernel, a successful container escape can grant an attackerroot access to the underlying host, and from there to every other container running on that machine. A single misconfigured Dockerfile can become the entry point for lateral movement across an entire cluster.The good news is that most Docker security failures are not caused by sophisticated zero-day exploits. They are caused by simple configuration mistakes that are easy to prevent once you know what to look for. If you are not sure where to start,scan your Dockerfile for freeto identify the most pressing issues in your current setup.Mistake 1: Running Containers as Root Without Thinking About ItThis is the single most common mistake we see, and it is also the most dangerous. By default, Docker runs containers as root. If an attacker exploits an application vulnerability inside the container, they already have root privileges inside that container environment. From there, container escapes become significantly easier.The fix:Create a dedicated non-root user in your Dockerfile and switch to it before the container starts.FROM node:18-alpine RUN addgroup -g 1001 -S nodejs && adduser -S nextjs -u 1001 WORKDIR /app COPY --chown=nextjs:nodejs . . USER nextjs EXPOSE 3000 CMD ["node", "server.js"] Notice theaddgroup and adduser commands, and the --chown flag on the COPY instruction. This ensures the application files are owned by the correct user, not root. The USER instruction tells Docker to run the container process as this user instead of root. Why This Matters More Than You ThinkRunning as root inside a container means that if your application has a remote code execution vulnerability, the attacker gets a root shell, not a limited user shell. Tools like Falco and Tracee detect container escapes, but they cannot prevent them if the container is already running with maximum privileges.Figure 1:Attack surface comparison — cluster with default-deny vs without. Default-deny blocks 90%+ of lateral movement and data exfiltration paths.Mistake 2: Using Bloated Base Images Because They Are ConvenientIt is tempting to base your image onubuntu:latest or debian:latest because everything is there. But every package you do not need is another potential vulnerability. The official Ubuntu image contains hundreds of packages, most of which your application will never use. The fix:Use minimal base images, and ideally multi-stage builds. We have a full guide onmulti-stage Docker builds for security and size optimizationif you want to dive deeper.# Build stage FROM node:18-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci --only=production # Production stage FROM node:18-alpine WORKDIR /app COPY --from=builder /app/node_modules ./node_modules COPY . . USER node EXPOSE 3000 CMD ["node", "index.js"] For even smaller attack surfaces, consider distroless images likegcr.io/distroless/nodejs18-debian11 or chainguard/node:latest. These images contain only your application and its runtime dependencies, with no shell, no package manager, and no unnecessary system utilities. Mistake 3: Ignoring Image Vulnerabilities Because "They Are Not My Code"Teams often scan their own application code but skip scanning the base image and its dependencies. This is a critical error. The vast majority of vulnerabilities in a container come from the operating system packages and libraries included in the base image, not from the application code itself.The fix:Integrate image scanning into your CI pipeline and enforce it at the registry level.# Scan with Trivy before pushing trivy image --exit-code 1 --severity HIGH,CRITICAL myapp:latest # Block vulnerable images at registry level with admission controls Set your pipeline to fail if HIGH or CRITICAL vulnerabilities are found. Do not let developers override this without a formal exception process. Vulnerabilities in base images arepatched regularly according to NVD disclosures, but only if you rebuild and redeploy. For a full vulnerability management workflow, check theDevSecOps checklist for containerized applications.Mistake 4: Hardcoding Secrets in the Dockerfile or Environment VariablesSecrets do not belong in Dockerfiles, environment variables, or shell scripts that get copied into the image. When you build a Docker image, every instruction creates a layer. Even if you delete a secret in a later layer, it still exists in the previous layer and can be extracted by anyone with access to the image.The fix:Use Docker BuildKit secrets or runtime secret injection.# syntax=docker/dockerfile:1.4 FROM node:18-alpine RUN --mount=type=secret,id=npmrc,target=/root/.npmrc \ npm ci --only=production Build with:DOCKER_BUILDKIT=1 docker build --secret id=npmrc,src=$HOME/.npmrc . At runtime, use Docker secrets (in Swarm) orKubernetes secrets, or better yet, integrate with a secret manager like HashiCorp Vault or AWS Secrets Manager. Never commit.env files or credential files to your repository. We cover this in more detail in our guide on Docker secrets management.Mistake 5: Using the "latest" Tag in ProductionThelatest tag is mutable. The image it points to today can be completely different from the image it pointed to yesterday. This makes debugging nearly impossible and introduces supply chain risk. The fix:Pin your base images to specific digests.FROM node:18.20.2-alpine3.19@sha256:7a3e...abcdef By pinning the SHA256 digest, you guarantee that every build uses the exact same base image. This is the only way to achieve reproducible builds. When you need to update the base image, you do it intentionally by updating the digest in the Dockerfile.Mistake 6: Leaving the Docker Daemon Socket ExposedThe Docker daemon socket at/var/run/docker.sock is owned by root. Any process that can write to this socket has full root access to the host. Mounting the Docker socket into a container is a common pattern for CI/CD pipelines, but it effectively disables container isolation for that container. The fix:Avoid mounting the Docker socket into containers whenever possible. If you must do it for CI/CD, use rootless Docker or a dedicated Docker-in-Docker approach with strict network isolation. Theofficial Docker documentation on rootless modecovers the setup process. Never mount the Docker socket in production application containers.Mistake 7: Running Containers with Privileged Mode EnabledPrivileged mode removes nearly all of Docker's security controls. It grants the container full access to host devices, disables seccomp and AppArmor profiles, and allows the container to load kernel modules. There is almost no legitimate reason to run a production container in privileged mode.The fix:Remove--privileged from your runtime commands. If your container needs specific capabilities, grant only those capabilities explicitly: docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE myapp Start with--cap-drop=ALL and add back only the capabilities your application actually needs. This is the principle of least privilege applied to containers. Mistake 8: Forgetting About Network SegmentationBy default, Docker containers on the same host can communicate with each other freely on the default bridge network. This means that if one container is compromised, the attacker can probe every other container on that host. A compromised web application container should not be able to reach your database container directly.The fix:Create custom bridge networks and isolate container groups.docker network create frontend docker network create backend docker run --network frontend myapp-web docker run --network backend myapp-db In production, useKubernetes network policiesor Docker network plugins that enforce micro-segmentation. Treat containers as if they are on separate subnets, because from a security perspective, they should be. For more multi-service hardening tips, refer to ourDocker Compose security guide.Mistake 9: Not Setting Resource LimitsWithout resource limits, a compromised container can consume all available CPU, memory, and disk I/O on the host, causing a denial of service for every other container. Resource limits are not just an operational concern; they are a security control.The fix:Always set memory and CPU limits.docker run -m 512m --cpus="1.5" myapp In Kubernetes, useresource requests and limits in your pod specifications. This prevents a single compromised container from starving the entire node.Mistake 10: Using ADD Instead of COPYTheADD instruction in Dockerfiles is more powerful than COPY, but that power comes with security risks. ADD can automatically extract tar archives and fetch files from remote URLs. If a remote URL is compromised, your build process will pull malicious content directly into your image. COPY is explicit and predictable. The fix:ReplaceADD with COPY everywhere unless you specifically need the extraction behavior. For remote files, download them in a separate step with curl or wget where you can verify checksums before copying into the image. Mistake 11: Copying the Entire Build Context Instead of Being SelectiveWhen you writeCOPY . /app, you copy every file in your build directory into the image. This often includes .git directories, .env files, local configuration, test data, and editor swap files. The fix:Use a.dockerignore file to exclude everything you do not need. .git .env .env.local node_modules *.log dist/ build/ .DS_Store .vscode/ Be explicit about what gets copied. Start with a deny-all approach and whitelist only the files your application needs.Mistake 12: Treating Images as Mutable After DeploymentIf an attacker compromises a running container and you simply restart it, you have not fixed the problem. You have just rebooted the compromised state. Container immutability means that a container should never be modified at runtime.The fix:Run containers with read-only filesystems where possible.docker run --read-only --tmpfs /tmp --tmpfs /var/cache myapp Use--read-only to prevent any runtime modifications to the container filesystem. Mount temporary directories as tmpfs only where the application needs writable space. In Kubernetes, set readOnlyRootFilesystem: true in the pod security context. Mistake 13: Not Logging Container ActivityWhen a container is compromised, you need forensic evidence to understand what happened. If you are not collecting container logs, audit trails, and runtime events, you are flying blind.The fix:Forward all container logs to a centralized logging system. Use tools likeFalcoor Tracee to detect anomalous runtime behavior, such as unexpected processes, file modifications, or network connections.Mistake 14: Running Outdated Docker Engine VersionsDocker Engine vulnerabilities are discovered regularly. Running an outdated Docker daemon is like running an outdated kernel. The container escape vulnerabilities of the past few years affected specific Docker versions, and the only fix was to upgrade.The fix:Establish a regular patching cadence for Docker Engine, containerd, and the host operating system. Test upgrades in a staging environment first, but do not let production versions drift more than one minor release behind the current stable version.Mistake 15: Assuming Container Isolation Is EnoughContainers provide process isolation, but they are not virtual machines. They share the host kernel, and a kernel vulnerability can break container isolation entirely. Teams sometimes deploy containers with the same security model they would use for VMs, which is insufficient.The fix:Layer additional security controls on top of container isolation. UseAppArmoror SELinux profiles to restrict what containers can do. Enable seccomp profiles to limit system calls. Use user namespaces to map container root to a non-root user on the host. These are not optional extras; they are essential layers of defense. For a deeper look at how compliance frameworks apply these controls, see ourNIST SP 800-190 compliance guide.Docker Security ChecklistUse this checklist before every production deployment:⬜ Container runs as a non-root user⬜ Base image is minimal and pinned to a specific digest⬜ Image has been scanned for HIGH and CRITICAL vulnerabilities⬜ No secrets are embedded in the image or environment variables⬜.dockerignore is configured and excludes sensitive files ⬜ Dockerfile usesCOPY instead of ADD ⬜ Resource limits are set for CPU and memory⬜ Container is not running in privileged mode⬜ Docker socket is not mounted inside the container⬜ Network is segmented and restricted to required connections⬜ Filesystem is read-only where possible⬜ Logs are forwarded to a centralized system⬜ Docker Engine and host OS are up to date⬜ AppArmor or SELinux profiles are enabled⬜ Runtime monitoring is active with Falco or equivalentFigure 2:Ports that teams commonly restrict. DNS (port 53) is almost always allowed; database and SSH ports are frequently restricted.Figure 3:Network policy feature support across Native K8s, Cilium, and ShieldOps.Figure 4:Progressive security posture stages. Only ~15% of clusters reach full zero-trust with audit.Frequently Asked QuestionsIs Docker inherently insecure?No. Docker provides strong isolation mechanisms, but their default configuration is permissive to avoid breaking existing applications. The security is in how you configure it, not in the default setup.Should I use Docker Secrets or a third-party secrets manager?Docker Secrets works well in Swarm mode, butKubernetes Secretsand external managers like HashiCorp Vault or AWS Secrets Manager provide more robust secret rotation and access control.How often should I rebuild my Docker images?At minimum, rebuild weekly to pick up security patches from your base image. Ideally, use automated CI/CD pipelines that rebuild on every commit plus a scheduled weekly rebuild regardless of changes.Can I run Docker in production without Kubernetes?Yes, but you lose orchestration-level security controls like network policies, pod security contexts, and admission controllers. For single-host production, use Docker Compose with the security practices outlined above.What is the single most important Docker security fix?Running containers as non-root. It eliminates the most common path to privilege escalation and container escape. Combine this with read-only filesystems and you block the majority of post-exploitation techniques.ConclusionDocker security is not a single configuration or a one-time audit. It is a continuous practice of reducing attack surface, enforcing least privilege, and verifying that your production environment matches your security intentions. The mistakes in this list are common because they are easy to overlook, not because they are difficult to fix.Start with the highest-impact fixes: run as non-root, scan your images, pin your base images, and stop embedding secrets. Then work through the checklist systematically. Every item you implement is one less path an attacker can use to move from a compromised container to your entire infrastructure.If you are unsure where your current Docker setup stands,start with a free automated scan. The vulnerabilities you find will tell you exactly which mistakes on this list are present in your environment, and that is where you should focus first. You can also explore ourfull library of security guidesfor more on DevSecOps, Kubernetes security, and compliance frameworks.📚Related:For end-to-end pipeline security, readCI/CD pipeline security: 15 best practices for 2026.#docker#security#best practicesReady to apply these concepts?Analyze your Dockerfile and find security vulnerabilities in seconds.Analyze Your Dockerfile NowRelated PostsDocker Secrets Management: Protecting API Keys and Credentials2026-06-08Docker Compose Security: Hardening Multi-Service Deployments2026-06-07Multi-Stage Docker Builds: Security and Size Optimization Guide2026-06-06Your takeRate this article or leave a commentShare Submit commentHave more questions? Check ourFAQ