Container image engineering

Writing a Production Dockerfile

Build reproducible container images with pinned inputs, multi-stage builds, non-root runtime users, clear startup behavior, and supply-chain evidence.

How this page is maintained

Written for learners, checked against the sources below, and reviewed every quarter. Last reviewed July 27, 2026.

Short answer

A production Dockerfile creates a reproducible, minimal runtime image from reviewed inputs. It separates build tools from runtime files, installs locked dependencies, runs as a non-root user, handles signals correctly, excludes secrets, and is scanned and tested before its immutable digest is promoted.

Who this is for: Application developers turning a working local container into a repeatable image suitable for reviewed production deployment.

  • Separate dependency, build, and runtime stages so production receives only required artifacts.
  • Pin and record image inputs while maintaining a deliberate update process for security fixes.
  • Test the built image as the actual runtime user with health, shutdown, and read-only constraints.

Choose reviewed and reproducible inputs

Start from an official or organization-approved base image that supports the runtime and architecture. Tags can move, while digests identify exact content. Pinning improves reproducibility, but an old pinned digest remains vulnerable until updated. Use an automated review process that proposes refreshed bases, runs tests, and records the resulting image digest.

Copy dependency manifests before application source when the build system can reuse cached layers safely. Install from lockfiles with commands that reject drift. Keep a narrow build context through an ignore file so source control metadata, local dependencies, credentials, test output, and unrelated large files never enter image layers or the builder unnecessarily.

Use stages to reduce the runtime

A builder stage may contain compilers, package managers, headers, and source. A runtime stage should receive only compiled output, production dependencies, certificates, and required operating-system files. Smaller images transfer faster and expose fewer packages, but minimalism must not remove timezone data, certificates, or debugging signals the application genuinely requires.

Combine commands based on logical cache and cleanup behavior, not a contest for the fewest layers. Verify package signatures through the normal ecosystem mechanism and avoid downloading executables from arbitrary URLs. Generate a software bill of materials when the delivery process supports it, then associate scan results and provenance with the immutable image.

Define a restricted runtime

Create or select an unprivileged user and ensure only required paths are writable. Do not bake secrets into environment instructions, arguments, copied files, or earlier layers because deleting them later does not remove layer history. Supply runtime secrets through the platform's approved secret mechanism and keep ordinary configuration separate.

Use an entry point that runs the application as the main process and forwards termination signals. Avoid shell wrappers that swallow signals unless they explicitly use process replacement and cleanup. Expose metadata documents intent but does not publish a port. Health checks should reflect deployment-platform ownership rather than being duplicated inconsistently in several layers.

Test and promote the artifact

Build in automation with a clean context, then run unit, integration, vulnerability, and policy checks against that image. Start it as the configured user, confirm it can serve a request, send a termination signal, and verify graceful shutdown within the platform deadline. Test with a read-only root filesystem when the workload should support one.

Promote by digest so staging and production use identical bytes. Sign or attest images according to organizational policy and restrict registry write access. Define severity and exception handling for scan findings rather than blocking blindly or ignoring all results. Rebuild after base, dependency, compiler, or certificate changes even when application code is unchanged.

Package a compiled web service

A service currently ships a two-gigabyte image containing source code, compilers, development dependencies, and a root runtime.

  1. Add an ignore file and a dependency stage that installs exactly from the committed lockfile.
  2. Compile in a builder stage, then copy only the server artifact, production dependencies, and needed certificates into a reviewed runtime base.
  3. Create a non-root user, set ownership only on required writable directories, and run the server directly as the main process.
  4. Build in CI, scan the resulting digest, test a request and graceful termination, then promote that digest through environments.
Result: The runtime image contains fewer packages and no development source, while its exact artifact and shutdown behavior are verified before release.

Production image review checklist

Apply this checklist to the Dockerfile and the resulting image digest.

  • Approved base, pinned digest, update owner, architecture support, and source provenance.
  • Lockfile install, narrow build context, multi-stage boundary, and runtime file inventory.
  • Non-root identity, filesystem permissions, secret exclusions, exposed port, and required writable paths.
  • Main process, signal handling, graceful shutdown, health behavior, and resource assumptions.
  • Tests, scan policy, software bill of materials, signature or attestation, registry access, and promoted digest.

Common mistakes

  • Copying a secret into one layer and deleting it later while it remains in image history.
  • Using a floating base forever without recording which bytes were tested and promoted.
  • Running a shell as the main process and discovering during deployment that termination never reaches the application.

Try one

Review a Dockerfile that uses a floating base tag, copies the whole repository before dependency installation, and runs as root. What changes should be prioritized?

A good answer selects a reviewed base and records its digest, narrows the build context, installs locked dependencies in a cache-friendly stage, copies only runtime artifacts, and configures a non-root user. It also tests signals, health, writable paths, and scanning. It does not claim image size alone proves security.

Sources

Learn this with a tutor

Tell LearnLive what you already know and what you need to do with production dockerfiles.

Build this course