Migrating from Alpine or Debian to a hardened container base image is more than changing the FROM line in a Dockerfile. The new runtime can affect entrypoints, health checks, package dependencies, debugging workflows, compiled binaries, and CI/CD pipelines. For teams making the move, the challenge is not simply choosing a new image. It is understanding what the application currently depends on and adapting those dependencies before the new image reaches production.
The migration is easiest to manage as a sequence of practical decisions: audit the images you use today, map each workload to an appropriate hardened target, adapt the build and runtime assumptions, roll the change out gradually, and then validate and enforce the new baseline. The same approach can be applied whether the target is CleanStart, Chainguard, Docker Hardened Images, Google Distroless, or another suitable hardened image provider.
Why Move Off Alpine and Debian Base Images
Alpine and Debian are widely used container bases because they provide familiar Linux environments and convenient tooling. That convenience can also mean carrying more packages and utilities into production than an application actually needs. NIST SP 800-190 recommends minimizing the contents of container images and eliminating unnecessary components, including shells and package managers, where they are not required.
Hardened base images address this by reducing unnecessary runtime surface and applying additional security controls to the image. The trade-off is operational: the same absence of a shell or package manager that reduces attack surface can remove tools your team currently relies on for troubleshooting or initialization.
A successful migration therefore starts by identifying those dependencies and moving them out of the production runtime where appropriate.
For a broader explanation of how minimal, hardened, and secure container images differ, see Minimal vs. Hardened vs. Secure Container Images: What's the Difference and Why It Matters for a breakdown of the security characteristics and trade-offs between these approaches.
Step 1: Audit Your Current Base Images
Start with a complete inventory of the base images in use across your services, not just the repositories your team remembers. Pull the FROM line from every Dockerfile, including application containers, sidecars, init containers, and CI build images, and record the exact tag or digest each one resolves to.
Then prioritize the migration backlog. Useful inputs include the number of known vulnerabilities in the current image, how many services depend on it, how exposed the workload is to untrusted input, and how critical the service is. An internet-facing API carrying multiple known vulnerabilities should generally be addressed before an internal batch job using the same base.
The output should be a backlog or inventory record for each image containing the current base and digest, consuming services, a vulnerability or package baseline, and a migration priority. You will use the same baseline later to measure whether the migration delivered the expected reduction in risk and image footprint.
Step 2: Map Each Image to a Hardened Equivalent
For each prioritized image, identify the target hardened base and document the changes required to make the workload run on it. Do not assume that a hardened image is a drop-in replacement for Alpine or Debian. The runtime contents, package names, C library, user configuration, entrypoint behavior, and debugging model can all differ.
A concrete image comparison can make these differences easier to evaluate. For example, see how the Official Go Docker image compares with a CleanStart hardened Go image to understand what changes at the image and runtime level.
If the target provider offers a development or debug variant, use it to reduce migration friction. Development variants may include a shell and package-management or troubleshooting tools that are intentionally absent from the production runtime. They can help teams identify dependencies before moving to the final hardened image.
The same principle applies to distroless-style targets that provide a debug variant. Validate application startup, health checks, file paths, certificates, permissions, and other runtime assumptions against the target environment before switching production traffic.
For every workload, document the mapping from the current image to the target image and record known breaking changes. This turns the migration from an open-ended image swap into a defined engineering task.
Step 3: Adapt the Build and Roll Out the Migration in Stages
Once the target image is mapped, adapt the Dockerfile and runtime configuration before changing production. The most common pattern is a multi-stage build: keep the full compiler, package manager, and build dependencies in the build stage, then copy only the application and the runtime dependencies into the final hardened image.
This also changes where security controls are applied in the delivery process. See where hardened container images fit in your CI/CD pipeline for a broader look at how the hardened image becomes part of the software delivery workflow.
Restructure the Dockerfile as a Multi-Stage Build
For a statically compiled Go service, the pattern can look like this:
# Stage 1: build, full toolchain, never ships to production FROM golang:1.22 AS build WORKDIR /src COPY . . RUN CGO_ENABLED=0 GOOS=linux go build -o /out/app ./cmd/app # Stage 2: hardened runtime FROM registry.example.com/hardened/base:1.0-runtime@sha256:<digest> COPY --from=build /out/app /app COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt USER nonroot ENTRYPOINT ["/app"] Two details in this example are important. First, the runtime image is pinned to a SHA256 digest rather than a mutable tag, which makes the exact artifact used by the build explicit. Second, the ENTRYPOINT uses exec form rather than shell form because a shell-free runtime image cannot interpret a shell-form command.
If the current image relies on an init script to run commands such as sed, grep, or cp before the application starts, do not assume those utilities will exist in the hardened runtime. Move that setup work into the build stage or, where it must happen at startup, consider a Kubernetes init container so the main application container can remain shell-free.
Roll the New Image Through Environments Gradually
Once the image builds and passes application tests, move it through environments gradually rather than switching production in a single cutover. A typical sequence is development, staging, a small production canary, and then a broader rollout. At each stage, monitor application errors, health checks, latency, resource usage, startup behavior, and any image-specific failures.
Define rollback thresholds before the canary begins. If error rates, latency, health checks, or other service-level indicators move outside the agreed range, automatically or quickly roll back to the previous image. The purpose of the canary is to expose runtime assumptions while the blast radius is still small.
Do not combine the base-image migration with an application or language-runtime upgrade. If both change at once and the workload regresses, it becomes much harder to determine which change caused the problem. Migrate the base image first, validate it, and make subsequent application changes separately.
After the initial rollout, keep the image lifecycle under the same discipline. Pin the production image by digest and automate update pull requests or equivalent workflows so new image releases go through the build, vulnerability scan, test, canary, and rollout process before they reach production.
Step 4: Validate and Enforce the New Baseline
Migration is not complete when the new image reaches 100 percent of production traffic. Compare the migrated image with the baseline captured during the audit and verify that the change delivered the expected security and operational improvements.
Measure concrete before-and-after values such as known CVE count, package count, image size, startup behavior, and, where available, the time required to remediate newly disclosed vulnerabilities. These measurements turn the migration into an outcome you can report and provide a basis for prioritizing the next workloads.
The final step is enforcement. Use admission or policy controls to prevent teams from quietly reintroducing older or unapproved base images. Depending on your environment, policies can require images to come from approved registries, be signed, use immutable digests, and meet defined vulnerability or provenance requirements.
Without enforcement, the migration can gradually erode as new Dockerfiles are created or existing ones are copied from older templates. The goal is not only to migrate the current fleet, but to make the hardened baseline the default for new workloads.
Once the hardened baseline is established, the next step is making it part of the normal development workflow. See how to insert hardened container images into CI pipelines for practical guidance on integrating hardened images into the build and delivery process.
CleanStart can be used as the target hardened image provider in this model. Its hardened images are built from source and designed for a reduced runtime footprint, with SBOMs, provenance, vulnerability management, and compliance-oriented options available for organizations that need additional assurance.
Common Migration Gotchas
Most migration issues fall into a small set of recurring categories. Reviewing these against your services before the first production canary can help identify changes that would otherwise surface during rollout.
Two details in this example are important. First, the runtime image is pinned to a SHA256 digest rather than a mutable tag, which makes the exact artifact used by the build explicit. Second, the ENTRYPOINT uses exec form rather than shell form because a shell-free runtime image cannot interpret a shell-form command.
If the current image relies on an init script to run commands such as sed, grep, or cp before the application starts, do not assume those utilities will exist in the hardened runtime. Move that setup work into the build stage or, where it must happen at startup, consider a Kubernetes init container so the main application container can remain shell-free.
Roll the New Image Through Environments Gradually
Once the image builds and passes application tests, move it through environments gradually rather than switching production in a single cutover. A typical sequence is development, staging, a small production canary, and then a broader rollout. At each stage, monitor application errors, health checks, latency, resource usage, startup behavior, and any image-specific failures.
Define rollback thresholds before the canary begins. If error rates, latency, health checks, or other service-level indicators move outside the agreed range, automatically or quickly roll back to the previous image. The purpose of the canary is to expose runtime assumptions while the blast radius is still small.
Do not combine the base-image migration with an application or language-runtime upgrade. If both change at once and the workload regresses, it becomes much harder to determine which change caused the problem. Migrate the base image first, validate it, and make subsequent application changes separately.
After the initial rollout, keep the image lifecycle under the same discipline. Pin the production image by digest and automate update pull requests or equivalent workflows so new image releases go through the build, vulnerability scan, test, canary, and rollout process before they reach production.
Step 4: Validate and Enforce the New Baseline
Migration is not complete when the new image reaches 100 percent of production traffic. Compare the migrated image with the baseline captured during the audit and verify that the change delivered the expected security and operational improvements.
Measure concrete before-and-after values such as known CVE count, package count, image size, startup behavior, and, where available, the time required to remediate newly disclosed vulnerabilities. These measurements turn the migration into an outcome you can report and provide a basis for prioritizing the next workloads.
The final step is enforcement. Use admission or policy controls to prevent teams from quietly reintroducing older or unapproved base images. Depending on your environment, policies can require images to come from approved registries, be signed, use immutable digests, and meet defined vulnerability or provenance requirements.
Without enforcement, the migration can gradually erode as new Dockerfiles are created or existing ones are copied from older templates. The goal is not only to migrate the current fleet, but to make the hardened baseline the default for new workloads.
Once the hardened baseline is established, the next step is making it part of the normal development workflow. See how to insert hardened container images into CI pipelines for practical guidance on integrating hardened images into the build and delivery process.
CleanStart can be used as the target hardened image provider in this model. Its hardened images are built from source and designed for a reduced runtime footprint, with SBOMs, provenance, vulnerability management, and compliance-oriented options available for organizations that need additional assurance.
Common Migration Gotchas
Most migration issues fall into a small set of recurring categories. Reviewing these against your services before the first production canary can help identify changes that would otherwise surface during rollout.
Gotcha | Why It Happens | Fix |
No shell in the container | Hardened images often remove /bin/sh and /bin/bash to reduce runtime attack surface. | Rewrite entrypoints and health checks to avoid shell-dependent syntax; use exec-form ENTRYPOINT/CMD. |
No package manager at runtime | Removing apk or apt prevents runtime package installation and reduces opportunities for post-exploit tooling. | Include required packages during the build and treat the running image as immutable. |
musl vs. glibc incompatibility | Alpine uses musl, while many Debian-based images use glibc. A binary or native dependency built for one environment may not run correctly in another. | Test native dependencies and recompile for the target environment, or choose a target image compatible with the application's requirements. |
BusyBox command differences | Some development or debug variants use BusyBox instead of GNU coreutils. | Check the commands and flags used by scripts and avoid relying on GNU-specific behavior unless it is guaranteed to be present. |
Package name drift across distros | Alpine, Debian, and other distributions use different package names and repository structures. | Create a package mapping during the migration and move build-time dependencies into the build stage. |
Entrypoint parsing differences | Shell-form ENTRYPOINT/CMD requires a shell such as /bin/sh, which may not exist in the production image. | Use exec-form JSON-array ENTRYPOINT/CMD and invoke the application directly. |
Init/setup scripts fail at container start | Startup scripts may assume sed, grep, cp, or other utilities are available in the runtime. | Move setup work into the build stage or use a Kubernetes init container where startup-time initialization is required. |
Non-root default changes behavior | Hardened images may run as a non-root user by default, affecting file permissions and privileged port binding. | Review file ownership and permissions during the build and use ports above 1024 where appropriate, or explicitly configure the required capability. |
No kubectl exec shell access | A shell-free production image cannot be accessed with the usual interactive shell workflow. | Use kubectl debug with an ephemeral debug container for live troubleshooting without changing the production image. |
Conclusion
Moving from Alpine or Debian to a hardened base image does not have to be a disruptive change. The teams that make the transition successfully tend to treat the base image as part of the application runtime rather than as a simple Dockerfile dependency. They understand what the current image provides, identify the assumptions that will change, test the new runtime in stages, and measure the result after the migration.
The specific hardened image provider matters, but the migration discipline matters just as much. Whether the target is CleanStart, Chainguard, Docker Hardened Images, Google Distroless, or another suitable provider, the same principles apply: understand the current image, account for runtime differences, test the workload before production, and make the hardened baseline part of the normal software delivery process.



