Who this is for
This is for the people who build, approve or run container images at a small or mid-sized company. That includes the engineer who writes the Dockerfile, the CTO who signs off on releases, and the IT or security lead who has to answer a customer questionnaire. If your team ships images to Kubernetes, a container service or a single VM, the same habits apply.
Image hygiene is not the same as pipeline setup. A pipeline can build and deploy a messy image every day. Hygiene is about what goes into the image, what is left out, how you identify it and how you prove where it came from. The goal is an image you can explain without opening a shell.
Use the Container image hygiene checklist one service at a time. Start with images that face the internet, images that run with extra privileges, and images nobody owns. You do not need a large platform team to make progress.
Before you begin
List every image your company builds and every third-party image you run. For each one, record the source repository, Dockerfile path, build owner, registry repository and the environment where it runs. If an image has no owner, that is your first finding.
Agree on a tag and digest policy before you change Dockerfiles. Decide which tags are immutable, how long you keep release images, and how deployments reference an image. Pick a scanner and an SBOM format. Trivy, Grype, Syft and cosign are common tools, but the names matter less than having one approved path.
Check that you can rebuild an image from its recorded commit and base digest. If the build depends on a laptop, a local file or an unrecorded build argument, fix that first. You also need registry admin access for retention and immutability settings.
Tell the team what happens when a scan fails. A gate with no agreed exception process gets bypassed. Name the person who can approve an exception, set an expiry date, and record why the risk is accepted.
The checklist
Run these checks before an image is pushed to a shared registry and again before a production release. Automate what you can in CI, then keep a short manual review for exceptions.
Treat every failure as a finding with an owner. A finding can be fixed, accepted with an expiry date, or used to block the release. Do not mark a scan as clean because the tool was noisy.
Keep the checklist in the repository next to the Dockerfile. A checklist in a wiki gets stale. A checklist in a pull request template gets read.
- Every image has a named owner, a source repository, a Dockerfile path and a recorded registry location.
- Each FROM line pins a base image by digest and keeps the readable tag in a comment for humans.
- The base image is the smallest one that works, such as distroless, scratch, slim or a vendor-supported minimal image, and a full OS image has a written reason.
- The build uses multiple stages so compilers, test tools, package caches and build scripts do not ship in the final image.
- A .dockerignore file excludes .git, local env files, node_modules, test data, logs and credentials from the build context.
- No secret is written into a layer by ENV, ARG, COPY or a RUN command, and build-time secrets use BuildKit secret mounts instead of layers.
- The final image runs as a non-root USER with a fixed UID and GID, and the runtime can use a read-only root filesystem.
- Package installs pin versions where practical, use --no-install-recommends, and remove package manager caches in the same layer.
- OCI labels record source, revision, version, created date, licenses and the build system.
- The same commit and the same pinned base digest rebuild to an image you can compare and audit, with lockfiles committed.
- A scanner runs on the built image before push, and the build fails on critical or high findings that have a fix available unless an approved exception exists.
- An SBOM in CycloneDX or SPDX is generated at build time and stored with the image or in the registry.
- The image is signed, and the deploy step verifies the signature and deploys by digest rather than a floating tag.
- Registry immutability, retention and access rules are set per repository, and release images are kept for the audit window.
What good looks like
A healthy image has a short, readable Dockerfile. The base is pinned, the final stage has the app and a certificate bundle, and nothing else. The container starts as a non-root user. You can look at the registry and see an SBOM, a signature and a digest that matches the deployment.
The team can answer four questions without guessing: what is in this image, who built it, from what commit, and what known issues remain? A scanner finding has an owner and a date. A new engineer can rebuild the image from the repository and get the same result.
Good hygiene also means the registry stays clean. Old test images do not pile up. Tags are not reused. A release image from last year can still be pulled for an audit, and an image with a serious flaw can be found across every environment by digest.
What to do next
Pick one service that matters. Run the checklist against its Dockerfile and registry entry. Fix the base digest, the user, the secrets and the scan gate first, because those give the largest reduction in risk.
Add the automated checks to CI as required steps. Put the manual items in the pull request template. Set a review date for every exception, and remove the exception when the fix lands.
If you want the platform around these checks built for you, Cloudverg's Delivery platform setup package is a fixed price of $14,500 and runs four weeks. It covers pipelines, environments, secret handling, monitoring and rollback, so your team can focus on image policy rather than wiring. It is one option among several, alongside doing the work in house or hiring a contractor.
Frequently asked questions
What is container image hygiene?
It is the set of choices that control what goes into an image, how small and predictable it is, and how you identify and verify it later. It covers base images, build stages, users, secrets, labels, scanning, SBOMs and registry rules.
Should I pin base images by digest or tag?
Pin by digest in the FROM line and keep the readable tag in a comment. A tag can move, but a digest points to exact bytes. You still need a process to review and update the digest on a schedule.
How do I stop secrets from ending up in image layers?
Keep secrets out of ENV, ARG and COPY. Use BuildKit secret mounts for build time and a runtime secret store for the container. Remove any secret that already shipped, rotate it, and rebuild the image rather than deleting a layer.