Cloud & Infrastructure

Containers & Images

Intermediate

Containers package an app with everything it needs, so it runs the same everywhere. Done well, they are reproducible and secure. Done carelessly, they ship known vulnerabilities, bloat, secrets, and root processes straight to production. Build small, pinned, non-root images from trusted bases, and scan them.

A container image is a build artifact like any other, and the same disciplines apply: know exactly what is in it, keep it minimal, do not bake in secrets, and keep it patched. The most common beginner mistakes are using a huge or untrusted base image, running as root, copying secrets into a layer, and never rebuilding to pick up security fixes.

Smaller images are not just faster. They have less attack surface and fewer vulnerabilities to track. Pinned, reproducible builds mean what you tested is what runs. Build and deploy images only through the pipeline (see CI/CD & Deployment). Never hand-build and push them.

Build images well

Fat, rooted, secret-laden FROM ubuntu:latest # huge, unpinned
COPY . . # copies .env with secrets
# runs as root by default

Unpinned and large (lots of CVEs), secrets copied into a layer that stays in the history forever, and running as root. This is a worst-case image on three counts.

Slim, pinned, non-root FROM mcr.microsoft.com/dotnet/aspnet:10.0@sha256:...
USER app # non-root
# secrets injected at runtime via managed identity, not copied in

Minimal pinned base, runs unprivileged, and no secret in the image. It is small, reproducible, and far less to attack or patch.

Run and maintain them

Self-review checklist

Why it matters: Containers are how our code reaches production, so an insecure image is an insecure deployment by default. Bloated, rooted, secret-carrying, or unpatched images are a direct supply-chain and runtime risk. Small, pinned, non-root, scanned images make every deployment reproducible and hard to attack.