How melange and apko Work Together
Originally published in Japanese at https://zenn.dev/ymotongpoo/books/chainguard-image-toolchain/viewer/35-melange-apko-integration.
The previous two hands-on chapters ran melange and apko separately. This chapter pulls together how the two relate, and summarizes what actually changes compared to a Dockerfile-based approach.
Where the division of labor sits
The boundary between melange and apko is clean. melange’s job is to fetch and build source code, then fix the result into the unit of an APK package. apko’s job is solely to combine those APK packages into an OCI image. apko having no way to run commands is a direct response to the problem we saw in Chapter 2 — that freedom of instructions lowers verifiability. Making that constraint hold requires pushing every operation that needs a command, such as compiling, over to melange’s side — and that’s exactly why the two tools are separate.
This division keeps apko’s YAML configuration remarkably easy to follow. Looking back at Chapter 7’s apko.yaml, all it says is which packages to install, from which repositories, with what startup configuration. All of the build’s complexity is concentrated on melange’s pipeline side.
The SBOM handoff from melange to apko
As we confirmed in Chapter 5, melange embedded an SPDX-format SBOM fragment inside the hello package at build time. When apko assembles an image, it detects this internal SBOM in each APK package being installed, and merges it into the image’s overall SBOM.
This works because both tools assume the same SBOM data model, SPDX. If melange didn’t generate an SBOM, apko would have to build its SBOM purely from package metadata (name, version, checksum), losing the detail of what was actually built inside that package. melange and apko can produce one consistent SBOM despite being separate tools precisely because they both honor this shared contract for the SBOM format.
Reproducibility is secured in two stages
The weakness in a Dockerfile’s reproducibility came from depending on the state of the repository at build time. With melange and apko together, reproducibility is secured in two stages.
First, at the melange stage, specifying a checksum like expected-sha256 on the pipeline’s fetch action pins the content of the fetched source code. The build environment itself is also specified as APK package versions in environment.contents, so the build toolchain’s composition follows the declaration too.
Then, at the apko stage, the combination of APK packages listed in contents.packages determines the layer contents. One thing worth noting here: apko.yaml itself declares package names, so the version resolved on any given build can still change if the repository index gets updated. This ambiguity has to be addressed either by pinning versions explicitly in contents.packages, or by rebuilding periodically in CI to keep drift within an acceptable range.
Where this leaves us
The combination of melange and apko lets the whole path from source code to container image be explained in a shared vocabulary: declarative YAML, and an SBOM generated from inside the build. The next chapter looks at how these two tools come together in Chainguard’s own production use.