APK Explained: Alpine Linux Package Manager

APK package manager: commands, world file, Docker gotchas

✅ Verified against the official Alpine Linux Wiki — Last updated: September 2026

APK (Alpine Package Keeper) is Alpine's own package manager — not related to Android's .apk despite the name collision. It's also the reason Alpine became the default base image for a huge share of Docker containers: fast, small, and simple. But apk has real differences from apt or pacman that catch people off guard, a recent major version change most tutorials haven't caught up to yet, and — when you use Alpine in containers specifically — some genuinely well-documented gotchas worth knowing before you commit to it. If you haven't installed Alpine yet, start with the installation guide.

Contents
  1. apk v3 — What Changed
  2. Core Commands
  3. The World File — Alpine's Desired State
  4. Subpackages and Auto-Install
  5. Repositories
  6. Alpine in Docker — Why It's Popular, and the Real Gotchas
  7. Troubleshooting
    1. Is apk the same as Android's APK format?
    2. Why does my container randomly fail to resolve a hostname that worked before?
    3. Why is my Python build so much slower on Alpine than on Debian?
    4. What's the difference between apk update and apk upgrade?
    5. How do I stop a specific package from ever being auto-installed as a dependency?
    6. Further Reading

apk v3 — What Changed

Since Alpine 3.23, the package manager itself runs on apk-tools v3 — a real transition, not a minor point release. It targets backward compatibility (the index and package formats are still v2), but if you're following an older tutorial, know that the tooling underneath has changed. New v3-specific subcommands exist (mkndx, mkpkg, adbsign for v3 packages and indexes), alongside conversion tools (convdb, convndx) for moving v2 databases to v3 format. Day-to-day commands like apk add work the same either way.

Core Commands

TaskCommand
Install a packageapk add <package>
Remove a packageapk del <package>
Search by name/descriptionapk search <term>
Package detailsapk info <package>
Refresh repository indexesapk update
Upgrade everything installedapk upgrade
Repair without changing selectionsapk fix

The World File — Alpine's Desired State

This is the part of apk that has no direct equivalent in apt or pacman. /etc/apk/world is a plain-text file listing the packages you actually want — your system's "desired state." apk add foo and apk del bar don't just install or remove files; they edit this file, then apk solves the rest.

# /etc/apk/world syntax: [!]NAME{@tag}{[<>~=]version}
git
nginx>=1.24
!some-package-you-never-want-auto-pulled-in

The ! prefix masks a package (blocks it from ever being auto-installed as a dependency), @tag pins a package to a specific tagged repository, and version operators like ~= pin a version range. If you edit /etc/apk/world by hand, run apk fix afterward to reconcile the actual system with the new desired state. One safety guarantee worth knowing: apk will never commit a change that leaves the system in an unsatisfiable or unbootable state — it backs out the change instead. Don't remove alpine-base or kernel packages from world manually; that's the one way to defeat that safety net.

Subpackages and Auto-Install

Alpine packages are split into subpackages more aggressively than most distros — a package's docs, shell completions, and service files often ship as separate subpackages rather than bundled in. You don't usually have to chase these down manually: apk uses an install_if rule to auto-install a relevant subpackage when its condition is met. Example: if zsh is already installed and you then install a package with a *-zsh-completion subpackage, apk pulls that subpackage in automatically. This is also why a fresh Alpine install can feel oddly bare — the docs meta-package that pulls in all documentation (including man pages) isn't installed by default.

Repositories

Configured in /etc/apk/repositories, one URL per line. The three you'll actually use:

  • main — officially supported packages, enabled by default.
  • community — community-maintained, still built by Alpine's own infrastructure, usually needs uncommenting in the repositories file.
  • testing — packages not yet promoted to main/community, less stable, opt-in.

After editing the repositories file, run apk update to refresh the index before installing anything from a newly enabled repo.

Alpine in Docker — Why It's Popular, and the Real Gotchas

Install it in an existing Linux host the normal way (apk add docker), but the more common scenario by far is using Alpine as the base image in a Dockerfile — small (a few MB), fast to pull, and it's what most tutorials default to. The size and speed are real. So are three specific, well-documented gotchas that catch people in production:

  1. DNS resolution failures under load. musl libc, by design, doesn't support DNS-over-TCP. Most lookups fit in a single 512-byte UDP packet and work fine — until a particular DNS response doesn't fit, which can happen intermittently depending on your DNS setup (Kubernetes environments hit this more than most). The failure mode is an intermittent "Unknown Host" error on a hostname that worked fine for months, which makes it genuinely hard to diagnose if you don't know the root cause.
  2. Smaller default thread stack size than glibc-based distros, which has caused crashes in some workloads (documented Python interpreter crashes are the most cited example) that don't happen on the same code under Debian or Ubuntu base images.
  3. Binary wheel / prebuilt binary incompatibility. Python wheels built against glibc don't run on musl, so pip has historically had to compile packages from source on Alpine — slower builds, and a need to manually install build dependencies (compilers, dev headers) that a glibc-based image gets for free via prebuilt wheels. This has genuinely improved since PEP 656 added official musl wheel support — many popular packages (NumPy, Pandas, matplotlib) now publish musl wheels — but coverage still isn't universal, so don't assume every dependency you need has one.

None of these make Alpine wrong for containers — they make it a real tradeoff, not a free lunch. It's the right call for a static Go binary or anything with minimal, well-tested dependencies. It's a worse call for a Python service with a heavy scientific-computing dependency tree, or anything where an intermittent DNS failure in production is unacceptable and you can't fully audit your resolution patterns.

Troubleshooting

Is apk the same as Android's APK format?

No relation beyond the acronym. Alpine Package Keeper predates widespread awareness of Android's .apk format as a general term and is an entirely separate package management system.

Why does my container randomly fail to resolve a hostname that worked before?

Classic musl DNS-over-TCP limitation — the DNS response for that particular hostname likely exceeded the 512-byte UDP packet size musl can handle. This is a known, documented limitation, not a bug in your application.

Why is my Python build so much slower on Alpine than on Debian?

Most Python packages ship prebuilt glibc wheels that don't work on musl, so pip falls back to compiling from source — much slower, and requires build tools and dev headers installed via apk add first. Check if your specific dependency has a musl wheel published (PEP 656) before assuming you have to compile it.

What's the difference between apk update and apk upgrade?

apk update refreshes the repository index (metadata about what's available) without installing anything. apk upgrade actually installs newer versions of packages already on your system, based on that refreshed index.

How do I stop a specific package from ever being auto-installed as a dependency?

Mask it in /etc/apk/world with a ! prefix (e.g. !some-package), then run apk fix to apply the change.


Go up

This site uses cookies for analytics and advertising (Google AdSense). By continuing to browse, you accept our use of cookies. Learn more