Container Security for Startups: What You Must Know in 2026

 


Most startups I work with run Docker in production. Very few of them scan the images before shipping. That gap is the single cheapest attack surface an early-stage company leaves exposed, and in 2026 it is being exploited more aggressively than any year prior. The recent Sysdig Cloud-Native Security and Usage Report found that 87% of container images contain high or critical vulnerabilities, and 66% of organisations have already experienced a security incident tied to an insecure container image. The gap between "we use Docker" and "we know what is inside our Docker images" is where breaches happen.

I am writing this for CTOs and heads of engineering at startups who have not yet built a container security practice, because the tooling to do it well is free, the pipeline integration takes a day, and the cost of not doing it keeps climbing. Three of the most exploited vulnerabilities of 2025 and 2026 — including CVE-2025-9074 in Docker Desktop that let a malicious container gain root on the host, and the Nvidia Container Toolkit flaw that allowed container escape — targeted the exact surface most startups leave unexamined. These are not theoretical risks. A Bellsoft survey in early 2026 found that nearly one in four developers had experienced a container breach in the last year, and 62% cited human error, mostly in image configuration, as the root cause.

This post covers the five things I would put in place at any startup running containers in production, in the order I would put them in.

1. Base image hygiene

The base image is the line in the Dockerfile that starts with FROM. It determines what operating system packages, what language runtime, and what default configuration ship with every container you run. The default choices most teams make — FROM node:latestFROM python:3FROM ubuntu — pull in full operating system images with hundreds of packages, most of which the application never uses. Every one of those packages is a potential CVE. The smaller the base image, the smaller the attack surface, and the fewer patches you need to chase.

The three options that matter for startups:

Alpine-based images are small (around 5 MB base) and widely supported. They use musl libc instead of glibc, which occasionally breaks Node.js native modules or Python scientific libraries, but for most web services they work fine.

Distroless images from Google contain only the application and its runtime dependencies — no shell, no package manager, no OS utilities. If an attacker compromises the container, there is nothing inside to work with. No bash, no curl, no apt. Distroless images are the strongest default for production services.

Chainguard images are a newer option: minimal, continuously rebuilt, and engineered specifically for zero-CVE shipping. Red Hat launched a similar catalogue in early 2026 called Project Hummingbird, aimed at the same problem. The Bellsoft survey found 48% of developers indicated pre-hardened vendor-maintained base images would be the single most effective mitigation for their container security problems.

The difference between a default image and a minimal one in practice:

# BEFORE: 900+ MB, ships with shells, package managers, OS utilities
FROM node:20
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
CMD ["node", "server.js"]

# AFTER: ~150 MB, no shell, no package manager, smaller CVE surface
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .

FROM gcr.io/distroless/nodejs20-debian12
WORKDIR /app
COPY --from=builder /app /app
USER nonroot
CMD ["server.js"]

Two things changed. The multi-stage build keeps build-time dependencies out of the final image. The distroless runtime strips the shell and package manager entirely. The USER nonroot line is important too: running containers as root means that if someone exploits the container, they have root inside it. That is the precondition for most container-escape attacks, and distroless images provide a nonroot user by default so there is no excuse not to use it.

2. Trivy for vulnerability scanning in CI

Trivy is the scanner I recommend every startup start with, and for most of them it is the only scanner they will ever need. It is open source under Apache 2.0, maintained by Aqua Security, and scans container images, filesystems, Git repositories, Kubernetes clusters, Terraform, CloudFormation, Dockerfiles, and Helm charts from a single binary. It has over 32,000 GitHub stars, and the vulnerability database is updated daily from NVD, GitHub Advisory Database, and vendor advisories.

The value proposition for a budget-conscious startup is direct: Trivy does at zero cost what commercial scanners charge per-developer or per-project fees for. Snyk Container starts at around $25 per developer per month, and pricing scales aggressively with team size. For a 15-person engineering team, that is $4,500 per year before any enterprise add-ons. Trivy is free, has no seat limits, no scan limits, no feature gates, and runs as a single binary in any CI system.

The minimum viable integration is a GitHub Actions job that builds the image, scans it, fails the build on critical or high vulnerabilities, and uploads results to the GitHub Security tab:

name: Container Security Scan
on: [push, pull_request]

jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Build image
        run: docker build -t myapp:${{ github.sha }} .

      - name: Scan image with Trivy
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: myapp:${{ github.sha }}
          format: sarif
          output: trivy-results.sarif
          severity: CRITICAL,HIGH
          exit-code: 1
          ignore-unfixed: true

      - name: Upload results to GitHub Security
        if: always()
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: trivy-results.sarif

      - name: Scan Kubernetes manifests
        uses: aquasecurity/trivy-action@master
        with:
          scan-type: config
          scan-ref: ./k8s/
          severity: CRITICAL,HIGH
          exit-code: 1

Two things to notice. exit-code: 1 on critical and high findings means a vulnerable image cannot be merged to main — the pipeline fails. ignore-unfixed: true skips CVEs that have no fix available yet, so the build does not fail on vulnerabilities the team cannot act on. The second job scans Kubernetes YAML for misconfigurations in the same pipeline: running as root, missing resource limits, privileged containers, host-path mounts, all caught before deployment.

When Snyk is worth the money: if the team needs automated fix pull requests, reachability analysis that tells you whether your code actually calls a vulnerable function, IDE plugins that surface findings as developers type, or a centralised dashboard across many repositories. For a startup under 30 engineers, Trivy in CI covers the 80% that matters. Revisit Snyk when the team has grown past that and the triage overhead starts outweighing the cost.

3. Secret scanning: before the commit, not after

The worst class of container security incident is the one where a developer bakes AWS credentials, a database password, or an API key into an image and pushes it to a public registry. It has happened to Uber, to Capital One, to countless startups nobody wrote news stories about. Secrets inside Docker images are not encrypted. docker history reveals every layer. A leaked image is a leaked credential.

The defence is layered and the layers are cheap:

Pre-commit hooks with gitleaks or trufflehog catch secrets before they ever reach the repository. Both are open source, run locally, and take under a minute to set up:

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.21.0
    hooks:
      - id: gitleaks

Install pre-commit once (pip install pre-commit && pre-commit install) and every commit on every engineer's machine gets scanned before it leaves local Git. The commit is rejected if gitleaks finds anything that looks like a credential.

Trivy secret scanning in CI catches what pre-commit missed — secrets introduced by engineers who skipped the hook, and secrets that live in base images or third-party dependencies:

- name: Scan filesystem for secrets
  uses: aquasecurity/trivy-action@master
  with:
    scan-type: fs
    scan-ref: .
    scanners: secret
    severity: CRITICAL,HIGH
    exit-code: 1

GitHub Secret Scanning is enabled for free on all public repositories and on private repositories with GitHub Advanced Security. It scans historical commits as well as new ones and notifies the affected service provider (AWS, Stripe, GitHub itself) when a known secret pattern is detected, who then revokes the credential automatically.

The rule is: secrets belong in a secret manager — AWS Secrets Manager, GCP Secret Manager, HashiCorp Vault, Kubernetes Secrets with encryption at rest — not in images, not in environment variables committed to Git, not in .env files checked into the repository. Pre-commit hooks plus CI scanning plus GitHub's native detection is three independent layers, each catching what the others miss.

4. Running containers with least privilege

Scanning tells you what is wrong with the image. Runtime configuration determines how much damage happens if something goes wrong anyway. The Bellsoft survey found that 62% of container security incidents trace back to human error in configuration. Most of those errors are the same handful of mistakes repeated across teams.

The default Docker configuration runs containers as root, with no seccomp profile restrictions, and no capability dropping. If an attacker compromises a process inside the container, they have root inside the container, which is the starting point for most escape attacks. The CVE-2025-9074 Docker Desktop vulnerability and the Nvidia Container Toolkit flaw both exploited this default to reach the host.

For any service running in Kubernetes, the pod specification should enforce the least-privilege defaults:

apiVersion: v1
kind: Pod
metadata:
  name: my-service
spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 1000
    fsGroup: 1000
    seccompProfile:
      type: RuntimeDefault
  containers:
    - name: app
      image: myapp:1.0
      securityContext:
        allowPrivilegeEscalation: false
        readOnlyRootFilesystem: true
        capabilities:
          drop: ["ALL"]
      resources:
        limits:
          memory: "512Mi"
          cpu: "500m"
        requests:
          memory: "256Mi"
          cpu: "250m"

runAsNonRoot: true and runAsUser: 1000 force the container to run as an unprivileged user. readOnlyRootFilesystem: true means malware cannot write to the filesystem to persist. capabilities.drop: ["ALL"] removes every Linux capability the container has by default — most application workloads need none of them. seccompProfile: RuntimeDefault restricts the system calls the container can make to a safe subset. allowPrivilegeEscalation: false blocks the sudo-like paths an attacker would use after gaining initial access.

These five lines block the majority of container-escape attack paths. Trivy's Kubernetes config scanner will flag a manifest that does not set them.

5. Continuous scanning of what is actually running

Scanning at build time catches known vulnerabilities in the code you ship today. It does not catch the vulnerability disclosed tomorrow in a dependency that was already in production. The Verizon 2025 Data Breach Investigations Report found that vulnerability exploitation as an initial attack vector increased 34% year over year, and 32% of exploited CVEs in the first half of 2025 showed activity on or before the day they were disclosed. The window between disclosure and weaponisation is measured in hours, not weeks.

Continuous scanning of running workloads means a new CVE disclosed on Tuesday is flagged in production environments on Tuesday, not when the next deployment happens on Friday. The Trivy Operator runs inside a Kubernetes cluster as an in-cluster scanner, continuously inspecting every deployed image and generating VulnerabilityReport resources that integrate with Prometheus and Grafana:

helm repo add aqua https://aquasecurity.github.io/helm-charts/
helm install trivy-operator aqua/trivy-operator \
  --namespace trivy-system \
  --create-namespace \
  --set trivy.ignoreUnfixed=true \
  --set operator.vulnerabilityScannerEnabled=true \
  --set operator.configAuditScannerEnabled=true

Once installed, the operator scans every workload in the cluster on a schedule and exposes the results as Kubernetes custom resources. A Grafana dashboard wired to those resources gives the engineering team a single view of every known vulnerability running in production, updated daily, with no ongoing operational cost.

The commercial equivalents — Snyk Monitor, Aqua Platform, Prisma Cloud — wrap the same functionality in a managed SaaS dashboard for several hundred to several thousand dollars per month. For a startup, the operator plus Grafana is enough.

Why this matters more in 2026 than it did two years ago

Trend Micro's 2026 predictions describe this year as the "true industrialisation of cybercrime": attack campaigns running autonomously from reconnaissance through data exfiltration, with poisoned open-source packages and malicious container images named explicitly as the most common vectors. Container supply-chain vulnerabilities grew 28% year over year, driven almost entirely by unverified open-source dependencies pulled into images without scanning. The attack surface is not shrinking, and the tools for exploiting it are getting cheaper and faster.

The five practices in this post — minimal base images, Trivy scanning in CI, layered secret detection, least-privilege runtime configuration, and continuous scanning of production workloads — are what I would put in place at any startup running containers, in that order, before I spent a dollar on a commercial security platform. The total tooling cost is zero. The engineering cost is a few days of configuration. The cost of not doing this, at the rate attackers are moving in 2026, is no longer theoretical.


Author note

Manjunaathaa

Associate DevOps Engineer at Frigga Cloud Labs. Works across AWS, GCP, and Azure daily with GitHub Actions as the deployment backbone. His focus is Proactive Resilience — keeping production systems secure and visible while keeping tooling cost low. Every practice in this post is something he actually runs in production, not something he read about.

This blog came from a pattern he keeps seeing in early-stage conversations: startups shipping Docker to production with no image scanning, no secret detection, and root containers running everywhere. The fix is not expensive. The fix is a weekend of configuration, and the rest is discipline.

Let's connect on LinkedIn → Manjunaathaa

Post a Comment

Previous Post Next Post