Container security is one of those topics where the gap between what teams think they are doing and what they are actually doing is enormous. In 2026, containers are the default deployment unit for most organizations, from startups shipping on Fly.io to enterprises running thousand-node Kubernetes clusters. But the security practices surrounding containers have not kept pace with their adoption.

After auditing container deployments across dozens of teams over the past few years, I have noticed the same patterns of failure repeating themselves. The mistakes are rarely exotic. They are mundane, predictable, and almost always preventable. This article covers the most common areas where teams get container security wrong and provides a practical checklist you can apply immediately.

Image Security: The Foundation Most Teams Crack

Your container image is the foundation of your security posture. Every vulnerability, every unnecessary binary, every hardcoded secret baked into that image becomes part of your attack surface. And yet, the state of image hygiene across the industry remains surprisingly poor.

The Base Image Problem

The first mistake teams make is choosing bloated base images. Using a full Ubuntu or Debian image as your base pulls in hundreds of packages you do not need: compilers, package managers, shells, network utilities. Each of these is a potential attack vector. If an attacker gains execution inside your container, those tools become their toolkit.

The fix is straightforward but requires discipline. Use minimal base images like Alpine, distroless images from Google, or Chainguard’s hardened images. A distroless image for a Go binary, for example, contains only the binary itself and the minimal system libraries it requires. There is no shell, no package manager, nothing an attacker can leverage for lateral movement.

I frequently encounter the argument that developers need full images for debugging. This is valid during development, but your production images should be stripped to the absolute minimum. Use multi-stage builds to compile in a full environment and copy only the final artifact into a minimal runtime image. This is not a new practice, but I am continually surprised by how many production deployments still ship with build tools included.

Image Scanning Is Necessary But Not Sufficient

Most teams have adopted some form of image scanning, typically through tools like Trivy, Grype, or their cloud provider’s built-in scanner. This is good, but many teams treat scanning as a checkbox rather than a process.

The common failure mode is scanning images at build time and never again. Vulnerabilities are discovered continuously. An image that was clean when you built it three months ago may have multiple critical CVEs today. You need continuous scanning of images in your registry and, ideally, of running containers.

Another gap is scan scope. Most scanners focus on OS-level packages and common language-specific dependencies. But your application’s transitive dependencies, vendored libraries, and statically linked binaries may not be fully covered. Ensure your scanning strategy covers the full dependency tree, not just the top-level packages.

A practical approach I recommend: scan at build time in your CI pipeline, scan images in your registry on a daily schedule, and alert on new critical and high-severity CVEs in images that are currently deployed. This three-layer approach catches vulnerabilities regardless of when they are discovered.

Image Signing and Provenance

Supply chain attacks against container images have moved from theoretical to practical. The Sigstore project and its Cosign tool have made image signing accessible, and in 2026, there is no excuse for not signing your production images.

Image signing establishes provenance: this image was built by this CI pipeline from this source commit. Combined with admission controllers like Kyverno or OPA Gatekeeper that enforce signature verification, you can ensure that only images built by your trusted pipeline run in your cluster.

Without signing and verification, anyone with push access to your registry can deploy arbitrary code. This includes compromised CI credentials, rogue team members, or an attacker who gains access to your registry. Signing does not prevent the image from being built, but verification prevents it from running.

Runtime Security: What Happens After the Container Starts

Image scanning tells you about known vulnerabilities before deployment. Runtime security tells you what is actually happening inside your containers after they start. The difference is crucial, and runtime protection is where most teams have the biggest blind spots.

Running as Root: The Default That Should Not Be

The single most common misconfiguration I encounter is containers running as the root user. Many official Docker images default to running their main process as root, and teams inherit this default without questioning it.

Running as root inside a container means that if an attacker exploits a vulnerability in your application, they immediately have root privileges within the container namespace. While container isolation should prevent escape to the host, privilege escalation exploits targeting the container runtime are discovered regularly. Running as a non-root user is a critical defense-in-depth measure.

The fix is to add a USER directive in your Dockerfile to switch to a non-root user before the entrypoint. In Kubernetes, enforce this with a securityContext setting runAsNonRoot: true at the pod or container level. Better yet, enforce it cluster-wide with a policy engine.

Capabilities and Seccomp Profiles

Linux capabilities are a fine-grained breakdown of the traditional superuser privileges. By default, Docker drops many capabilities but retains a set that is more permissive than most applications need. Kubernetes defaults are even more permissive in some configurations.

The best practice is to drop all capabilities and add back only what your application specifically requires. Most web applications need no special capabilities at all. An application that needs to bind to a privileged port below 1024 can be granted NET_BIND_SERVICE alone, rather than running with the full default set.

Seccomp profiles restrict which system calls a container can make. The default Docker seccomp profile blocks roughly 44 of the 300-plus syscalls available on Linux, which is a reasonable baseline. But a custom seccomp profile tailored to your application can reduce the attack surface dramatically. Tools like strace or eBPF-based profilers can help you determine which syscalls your application actually uses, allowing you to build a whitelist profile.

Network Policies: The Forgotten Firewall

In a default Kubernetes installation, every pod can communicate with every other pod. There are no network boundaries within the cluster. This means that if an attacker compromises one container, they can reach every service in your cluster, including your database, your internal APIs, and your control plane components.

Network policies are the Kubernetes equivalent of firewall rules. They let you specify which pods can communicate with which other pods, on which ports, and in which directions. Despite being a fundamental security control, I find that a majority of clusters I audit have either no network policies or only partial coverage.

Start with a default-deny policy for all namespaces, then explicitly allow the traffic patterns your applications require. This inverts the security model from permissive-by-default to restrictive-by-default, which is the correct posture for any production system.

Read-Only File Systems

Another frequently overlooked hardening measure is running containers with a read-only root filesystem. If an attacker cannot write to the filesystem, they cannot drop malware, modify configuration files, or establish persistence. Most applications can run with a read-only filesystem if you provide writable tmpfs mounts for temporary directories they need.

In Kubernetes, set readOnlyRootFilesystem: true in the container’s security context. Then add emptyDir volumes for /tmp, /var/run, or any other directories your application writes to. This simple change eliminates an entire category of post-exploitation techniques.

Supply Chain Security: Trusting Your Dependencies

Container supply chain security extends beyond your own images. Every base image, every package, every library you pull into your container is a link in a chain of trust. In 2026, supply chain attacks are among the most common vectors for compromising containerized applications.

Pinning and Reproducibility

Using latest tags for base images is a well-known anti-pattern, yet it persists. When you use FROM node:latest, your build is not reproducible. The image you get today may differ from the image you get tomorrow, and you have no guarantee that a future version has not been compromised or does not introduce a breaking change.

Pin your base images to specific digests, not just tags. Tags can be moved to point to different images, but a digest is a cryptographic hash that uniquely identifies an image. This guarantees reproducibility and protects against tag manipulation attacks.

The same principle applies to package installation. Lock your dependency files, pin versions, and verify checksums where possible. Every unpinned dependency is a supply chain risk.

Private Registries and Access Control

Your container registry is a critical piece of infrastructure. If an attacker can push a malicious image to your registry, they can potentially get it deployed to your production cluster. Ensure your registry has strong access controls, audit logging, and image immutability policies that prevent tag overwrites.

For sensitive environments, consider running a private registry with image promotion workflows. Images are built and scanned in a staging registry, then promoted to a production registry only after passing all security checks. This creates a clear audit trail and prevents unvetted images from reaching production.

Secrets Management: Stop Putting Credentials in Images

Hardcoded secrets in container images are a persistent problem. API keys, database passwords, and TLS certificates baked into Docker images end up in registry caches, build logs, and layer histories. Even if you delete a secret in a later Dockerfile layer, it remains accessible in the image’s layer history.

The correct approach is to inject secrets at runtime through your orchestrator’s secrets management. Kubernetes Secrets, while base64-encoded rather than encrypted by default, can be backed by external secret stores like HashiCorp Vault, AWS Secrets Manager, or the External Secrets Operator. The key principle is that secrets should never touch your image build process.

A common mistake in Kubernetes specifically is mounting secrets as environment variables rather than files. Environment variables can leak through process listings, crash dumps, and logging frameworks. Mounting secrets as files in a tmpfs volume is more secure and allows for secret rotation without restarting the pod.

Common Kubernetes Misconfigurations

Beyond the container-level security issues, Kubernetes itself introduces a large configuration surface with many security-relevant defaults that need attention.

RBAC Overprovisioning

Role-Based Access Control in Kubernetes is powerful but complex. The most common mistake is granting overly broad permissions because it is easier than figuring out the minimal set. Service accounts with cluster-admin privileges, wildcard resource permissions, and unused role bindings accumulate like technical debt.

Audit your RBAC policies regularly. Use tools like rbac-lookup or kubectl auth can-i --list to understand what each service account can actually do. Apply the principle of least privilege rigorously: every service account should have only the permissions it needs, scoped to the narrowest possible namespace.

The Default Service Account

Every Kubernetes namespace has a default service account that is automatically mounted into every pod unless you explicitly opt out. This service account often has more permissions than necessary, and its token is accessible to any process running in the pod.

Disable automatic service account token mounting by setting automountServiceAccountToken: false on the service account or pod spec. Create dedicated service accounts for pods that genuinely need Kubernetes API access, and grant them only the specific permissions required.

Etcd Encryption

Kubernetes stores all cluster state, including Secrets, in etcd. By default, Secrets are stored base64-encoded but not encrypted at rest. Anyone with access to the etcd data directory can read every secret in your cluster.

Enable encryption at rest for etcd. Most managed Kubernetes services handle this automatically, but if you are running self-managed clusters, this is a critical configuration that is frequently overlooked.

A Practical Security Checklist for Small Teams

Security guidance often feels overwhelming, especially for small teams without dedicated security engineers. Here is a prioritized checklist you can work through incrementally, ordered by impact relative to effort.

  • Use minimal base images (Alpine or distroless) and multi-stage builds. Effort: low. Impact: high.
  • Run containers as non-root. Add USER directives and enforce runAsNonRoot. Effort: low. Impact: high.
  • Enable image scanning in CI with Trivy or Grype. Block deployments with critical CVEs. Effort: low. Impact: medium.
  • Pin base images by digest and lock dependency versions. Effort: low. Impact: medium.
  • Implement default-deny network policies in every namespace. Effort: medium. Impact: high.
  • Drop all Linux capabilities and add back only what is needed. Effort: medium. Impact: medium.
  • Use read-only root filesystems with targeted writable mounts. Effort: medium. Impact: medium.
  • Sign images with Cosign and enforce verification with an admission controller. Effort: medium. Impact: high.
  • Externalize all secrets through a secrets manager. Never bake secrets into images. Effort: medium. Impact: high.
  • Audit RBAC policies quarterly. Remove unused bindings and narrow permissions. Effort: medium. Impact: medium.
  • Disable default service account token mounting. Create purpose-specific service accounts. Effort: low. Impact: medium.
  • Enable continuous registry scanning on a daily schedule. Alert on new CVEs in deployed images. Effort: low. Impact: medium.

You do not need to implement everything at once. Start with the top four items, which require minimal effort and provide immediate security improvement. Then work through the rest as your team’s capacity allows.

The Cultural Dimension

The hardest part of container security is not technical. It is cultural. Security measures that slow down development velocity will be worked around. Configuration that is complex will be misconfigured. Policies that are not understood will be ignored.

The most effective security teams I have worked with embed security into the development workflow rather than bolting it on as a gate. Automated scanning in CI pipelines, policy-as-code with clear error messages, golden path templates that are secure by default, and regular security-focused retrospectives on incidents and near-misses.

If your developers see security as someone else’s problem, no amount of tooling will save you. If they understand why these controls exist and have ergonomic tools to comply with them, your security posture will improve organically.

Conclusion

Container security in 2026 is not about exotic zero-day defenses or bleeding-edge technology. It is about consistently applying well-understood practices: minimal images, non-root execution, proper secrets management, network segmentation, and supply chain verification. The teams that get container security right are not the ones with the biggest budgets. They are the ones that treat security as an integral part of their development and operations workflow, not an afterthought.

Start with the checklist, automate what you can, and build security awareness into your team’s culture. The threat landscape will keep evolving, but getting the fundamentals right protects you against the vast majority of real-world attacks.

By

Leave a Reply

Your email address will not be published. Required fields are marked *