# Wolfi OS and APK Packages

> Source: https://www.ymotongpoo.com/books/chainguard-image-toolchain/10-wolfi/


Before getting into melange and apko, let's look at **Wolfi**, the material both tools share. Wolfi is a Linux distribution developed by Chainguard. For package management, it uses the APK format that Alpine Linux adopted.

## Positioned as an "undistro"

Wolfi's GitHub repository, [wolfi-dev/os](https://github.com/wolfi-dev/os), describes itself as a "Linux undistro." Where a typical distribution provides a complete OS, down to a specific kernel version, init system, and even a desktop environment, Wolfi only provides a set of packages and a build foundation, on the assumption that it will be used as material for container images.

Alpine Linux has long been widely used as a container base image for its light weight, but because it adopts `musl` as its standard C library, binaries built assuming `glibc` sometimes don't run on it as-is[^musl]. Wolfi is `glibc`-based, so it can use Alpine's APK package management mechanism while keeping compatibility with existing Linux binaries.

[^musl]: See also https://aws.amazon.com/jp/builders-flash/202502/base-img-for-container-minimization/.

## Why not just use Alpine or Ubuntu as-is

As Chapter 1 mentioned, Chainguard built a new distribution, Wolfi, instead of using Alpine Linux, Ubuntu, or Debian. Here's a quote from Chainguard's official blog post, "[Reimagining the Linux distro with Wolfi](https://www.chainguard.dev/unchained/reimagining-the-linux-distro-with-wolfi)," on the reasoning behind that decision:

> Linux distributions — such as Red Hat and Ubuntu — haven't changed much in the past couple of decades. They were originally designed for running on servers in a physical rack.

The post goes on to point out that the container runtime environment simply doesn't match that original design premise:

> In a container, you're generally only running a single application, so you need much less "stuff." Static binaries are often a better solution than shared libraries. ... Both of these things are contrary to the founding principles of many Linux distributions.

Packages in general-purpose distributions are built on the assumption that multiple applications coexist on a server and share libraries. Containers, however, usually put only a single application in an image, so there's no reason to follow that assumption. Reusing an existing distribution's packages as-is drags in features and dependencies a container never uses, feeding directly into the attack-surface problem covered in Chapter 2.

The post cites another practical benefit of having your own distribution:

> Having a distro like Wolfi also meant that we could issue security advisories, which are used by scanners and similar when identifying vulnerabilities in containers.

If you use an existing distribution's packages as-is, the source of vulnerability information also depends on that distribution. Wolfi being able to issue its own security advisories as a distribution means that the SBOMs apko generates, and scanning tools, can determine the presence of vulnerabilities accurately, based on Wolfi as a single source of truth.

## Where the kernel and the build environment start

So far we've looked at what kind of distribution Wolfi is, but to begin with, a container doesn't include an OS kernel at all. A container is a mechanism that isolates and runs processes while sharing the host's kernel, using the Linux kernel's `namespaces` (isolating things like process space and networking) and `cgroups` (resource limits). Wolfi-based images are no exception: running `uname -a` inside a container doesn't return Wolfi's kernel — it returns the kernel version of the host actually running it (or a VM carrying the same kernel as the host). In other words, there's no such thing as "Wolfi's kernel" in the first place.

So what is Wolfi's "smallest foundation that can't be broken down further"? It's a package called `wolfi-base`. Looking at the definition in [wolfi-base.yaml](https://github.com/wolfi-dev/os/blob/main/wolfi-base.yaml), the package itself is an empty metapackage that owns no files at all — its substance is just the three packages listed under `dependencies.runtime`:

```yaml
package:
  name: wolfi-base
  dependencies:
    runtime:
      - apk-tools
      - busybox
      - wolfi-keys
```

`apk-tools` is the APK package manager itself, `busybox` is a shell and a set of coreutils-equivalent commands, and `wolfi-keys` provides the public keys used for signature verification. With just these three, you're in a state where you can add whatever packages you need with the `apk` command.

That raises one more natural question. melange, the tool that builds packages, itself assembles its build environment from APK packages (that's `environment.contents`, covered in Chapter 4). So how is that very first build environment prepared? This is a chicken-and-egg problem: the environment used to build packages is itself built from packages.

There's no single official document that spells this out, but the implementation reveals a two-part solution. One part is using the existing APK ecosystem Alpine Linux already has as a seed. In fact, the example build environment in melange's README points directly at Alpine's official repository, `https://dl-cdn.alpinelinux.org/alpine/edge/main`, rather than Wolfi. The other part is the several `*-bootstrap.yaml` files, such as `git-bootstrap.yaml` and `cmake-bootstrap.yaml`, that exist in the [wolfi-dev/os](https://github.com/wolfi-dev/os) repository. For complex packages like compilers and toolchains, the chicken-and-egg problem is avoided through staged self-hosting: build a simplified version first, then use that simplified version to build the production version.

## Every package is built with melange

Wolfi's package definitions live in the [wolfi-dev/os](https://github.com/wolfi-dev/os) repository, where each package corresponds to its own `melange.yaml`, such as `curl.yaml` or `openssl.yaml`. In other words, Wolfi doesn't pull in and repackage existing packages the way a general-purpose distribution does — instead, it builds the software it needs from source with melange, each time, as needed. The resulting APKs are signed and published as the APK repository at `https://packages.wolfi.dev/os`[^repo].

[^repo]: Opening this URL directly in a browser returns a `NoSuchKey` error. This isn't a misconfiguration — it's how a GCS (Google Cloud Storage) bucket behaves. `https://packages.wolfi.dev/os` doesn't point to a single object named "os"; it's a prefix that apk, apko, and melange use to reach specific files, such as `https://packages.wolfi.dev/os/x86_64/APKINDEX.tar.gz`. If you want to list the bucket's contents, visiting `https://packages.wolfi.dev/` without the trailing path returns XML containing the package listing.

Chapter 4 covers melange's mechanics in detail, but the key relationship to hold onto here is: Wolfi's packages *are* melange's build output. Learning melange in the chapters that follow is, in effect, learning how Wolfi's packages are built.

## Wolfi as seen from apko

apko specifies APK repository URLs in `contents.repositories` in `apko.yaml`, and fetches packages from there to assemble an image. Many Chainguard Images are defined by pointing `repositories` at Wolfi's public repository and listing the packages they need (`wolfi-base` and individual runtimes, among others) in `contents.packages`. Chapter 6 covers apko's configuration in detail.

To sum up: Wolfi is the source of APK packages, melange is the build tool responsible for supplying them, and apko is the tool that consumes those supplied packages to assemble an image. Starting in the next chapter, we'll look at melange, the starting point of this relationship.

