Why CleanStart Chose APK Instead of dpkg/rpm
Most container images use dpkg (Debian) or rpm (Red Hat) as their package manager. Both work, but they don't let you split a single package into subpackages—you install the whole thing. So if you want just the libc runtime without the C compiler, you're out of luck. APK (Alpine Package Keeper) is different: it's specifically designed with subpackages, letting you install only what you need. CleanStart uses APK's subpackage system paired with GLIBC (not Alpine's musl), so you get minimal images while maintaining full compatibility with GLIBC-based Linux systems. The result: container images 70% smaller without sacrificing compatibility.
APK (Alpine Package Keeper) is a package manager—a tool for installing, removing, and managing software packages in Linux. CleanStart uses the APK format with GLIBC (not Alpine's musl), gaining APK's superior subpackage granularity while maintaining production compatibility.
What people assume: APK = Alpine Linux = musl libc = small but incompatible What CleanStart actually does: APK package format + GLIBC runtime = stripped-down AND compatibleThis combination is the core of CleanStart's image construction strategy: use APK's superior package granularity to achieve minimal images while keeping GLIBC's production compatibility.
The compatibility promise: Because CleanStart uses GLIBC, anything that runs on a GLIBC-based Linux (Debian, Ubuntu, RHEL, Fedora, SUSE, Amazon Linux, and others) runs on CleanStart. Your existing binaries, your language runtimes, your database drivers, your monitoring agents — they all work unchanged. APK is the packaging mechanism underneath; GLIBC is the runtime contract that guarantees compatibility.
1Traditional Package Managers2APK - Subpackage Approach3CleanStart StrategyWhy Not dpkg or rpm?
Every Linux distribution has a package manager. The three major ones are dpkg/apt (Debian, Ubuntu), rpm/dnf (RHEL, Fedora, SUSE), and apk (Alpine, Wolfi, CleanStart).
The critical difference is subpackages — and this is why CleanStart chose APK.
The Subpackage Advantage
What is a Subpackage?
In traditional package managers (dpkg, rpm), a software package is distributed as a single unit. When you install OpenSSL on Debian, you get everything — headers, development files, documentation, configuration tools, and the runtime library — all bundled together, or split into coarse packages like libssl3, libssl-dev, and openssl.
APK takes a fundamentally different approach. A single source package can produce many subpackages, each containing only a specific subset of files. In traditional package managers like dpkg/apt with coarse splitting, the openssl source package produces three packages: openssl (CLI tools, config files, docs at 2.1 MB), libssl3 (runtime shared library at 1.8 MB), and libssl-dev (headers and static libraries at 3.2 MB). This totals 3 packages, but the minimum install for runtime is libssl3 (1.8 MB, though it includes things you may not use).
APK takes a fine-grained subpackage approach. The openssl source APKBUILD produces nine subpackages: openssl (CLI tool only at 180 KB), openssl-doc (man pages and documentation at 340 KB), openssl-dev (headers for compilation at 890 KB), openssl-libs-static (static libraries at 2.1 MB), libssl3 (libssl shared library only at 420 KB), libcrypto3 (libcrypto shared library only at 1.2 MB), openssl-config (default configuration at 12 KB), openssl-c_rehash (certificate rehash script at 8 KB), and openssl-dbg (debug symbols at 4.5 MB). The minimum install for runtime is libssl3 plus libcrypto3 (just 1.62 MB for the .so files).
With APK subpackages, CleanStart installs only the exact shared libraries an application needs — not the CLI tools, not the headers, not the documentation, not the debug symbols.
Real Impact: PostgreSQL
Here's what a PostgreSQL server image looks like with different package managers:
Debian (dpkg) — PostgreSQL 16: postgresql-16 → Server binary + configs + scripts (16.8 MB)libpq5 → Client library (220 KB)postgresql-client-16 → psql CLI + utilities (3.2 MB)postgresql-common → Cluster management scripts (1.4 MB) Minimum for running PostgreSQL server: ~21.6 MB of packagesBut: postgresql-16 includes pg_dump, pg_restore, initdb, pg_ctl, locale data, timezone compiler, regression test scripts... Most of which a running server doesn't need.APK (CleanStart) — PostgreSQL 16: postgresql16 → Core server binary (4.2 MB)postgresql16-openrc → Init scripts (separate, not needed in container)postgresql16-client → psql + pg_dump etc (separate, not in prod image)postgresql16-contrib → Extensions (separate, install only what you use)postgresql16-dev → Headers (separate, build-stage only)postgresql16-doc → Documentation (separate, never in image)postgresql16-libs → libpq shared library (180 KB)postgresql16-jit → JIT compilation support (separate, optional)postgresql16-plpython3 → Python procedural language (separate, optional)postgresql16-plperl → Perl procedural language (separate, optional)postgresql16-pltcl → Tcl procedural language (separate, optional) Minimum for running PostgreSQL server: postgresql16 + postgresql16-libs = 4.38 MBEvery extension, language binding, and tool is a separate subpackage.Result: CleanStart's PostgreSQL image installs approximately 80% less package content than a Debian-based equivalent because APK subpackages let you exclude components at a granularity that dpkg simply can't match.
How Subpackages Enable Strip-Down
The APKBUILD File
Every APK package is defined by an APKBUILD file — a shell script that describes how to compile the software and how to split the output into subpackages. The key insight is that each nginx module is its own subpackage. A production image that serves static files doesn't need the stream module, the mail module, or the Perl module. With APK, you install exactly the modules you use.
Subpackage Naming Convention
APK subpackages follow a predictable naming pattern. The base package name is used for the core runtime binary or library. The -dev suffix denotes headers and build files, which are only needed at build time. The -doc suffix marks man pages and documentation. The -dbg suffix indicates debug symbols, which are stripped from release builds. The -openrc suffix identifies OpenRC init scripts, which are not needed in containers. The -libs suffix separates shared libraries from the main package. The -static suffix marks static libraries. The -lang suffix includes locale and translation files. The -bash-completion suffix provides shell completions. The -{module} pattern identifies optional modules or plugins.
This convention means CleanStart can systematically exclude -dev, -doc, -dbg, -openrc, -static, -bash-completion, and -lang subpackages from production images — automatically stripping out everything a running container doesn't need.
APK vs dpkg: Side-by-Side Comparison
Installing Python 3.12
dpkg/apt (Debian): $ apt install python3.12 python3.12 14.2 MB (interpreter + stdlib + __pycache__) libpython3.12 6.1 MB (shared library) python3.12-minimal 5.3 MB (can't go smaller than this) Smallest possible: python3.12-minimal = 5.3 MBBut: still includes test modules, idle references, full stdlibAPK (CleanStart): $ apk add python3 python3 4.1 MB (interpreter + minimal stdlib) Optional subpackages (install ONLY what you need): python3-dev 890 KB (headers — build stage only) python3-doc 2.1 MB (documentation — never in image) python3-tests 3.4 MB (test suite — never in image) python3-tkinter 580 KB (GUI — never in server container) python3-dbg 8.2 MB (debug build — never in prod) python3-idle 320 KB (IDE — never in container) py3-pip 1.8 MB (pip — build stage only) py3-setuptools 940 KB (setuptools — build stage only) Smallest possible: python3 = 4.1 MBAnd: test suite, tkinter, idle, debug symbols are truly absentInstalling Node.js 20
dpkg/apt (Debian): $ apt install nodejs nodejs 31.2 MB (runtime + npm + docs + man pages) No way to install node without npm using apt.No way to exclude man pages.APK (CleanStart): $ apk add nodejs nodejs 18.4 MB (runtime binary only) Optional (separate subpackages): nodejs-doc 1.2 MB nodejs-dev 340 KB npm 6.8 MB (npm is a SEPARATE package entirely) yarn 1.4 MB Install node WITHOUT npm: possible and default.The Strip-Down Pipeline
CleanStart's image build process uses APK subpackages in a systematic pipeline. Step 1 compiles from verified source, with APKBUILD splitting output into fine-grained subpackages. Step 2 analyzes dependencies by determining which subpackages the application actually links against, using ldd and readelf to identify required .so files, and mapping those files back to APK subpackages. Step 3 installs only required subpackages using apk add --no-cache {only-runtime-subpackages}, excluding -dev, -doc, -dbg, -openrc, -static, -lang, and -tests packages. Step 4 verifies nothing is missing by running the application binary, checking that all shared library dependencies resolve, and executing the 78-test inspection suite. The result is a minimal image with only the code that actually executes.
Concrete Example: Redis
Full Redis package set (all subpackages): redis 1.8 MB ← server binary redis-cli 420 KB ← client CLI redis-sentinel 1.1 MB ← sentinel binary redis-benchmark 380 KB ← benchmarking tool redis-dev 120 KB ← headers redis-doc 890 KB ← documentation redis-openrc 8 KB ← init scripts CleanStart production Redis image installs: redis 1.8 MB ← just the server NOT installed (saved 2.9 MB + attack surface reduction): redis-cli ← not needed (use kubectl exec or separate debug pod) redis-sentinel ← separate image if needed redis-benchmark ← never in production redis-dev ← build-stage only redis-doc ← never in image redis-openrc ← containers don't use OpenRCAPK + GLIBC: The CleanStart Combination
The common misconception is that APK requires Alpine Linux and therefore requires musl. This is not true.
CleanStart builds GLIBC-based APK packages. The APKBUILD system compiles software against GLIBC, and the resulting .apk files contain GLIBC-linked binaries:
Traditional Alpine approach: APK packages → compiled against musl → Alpine base image (musl) Result: Small but incompatible with many production workloads CleanStart approach: APK packages → compiled against GLIBC → CleanStart base image (GLIBC) Result: Small AND compatible with all production workloadsThis gives CleanStart both advantages simultaneously: Subpackage granularity (Yes), GLIBC compatibility (Yes), Enterprise DNS/NSS (Yes), Thread safety (Java) (Yes), Minimal image size (Yes), Locale support (Full), FIPS 140-3 support (Yes), and Fine-grained strip-down (Yes).
CleanStart gets Alpine-class image sizes with full GLIBC compatibility.
If It Runs on Any GLIBC-Based Linux, It Runs on CleanStart
This is the single most important thing to understand about CleanStart's APK + GLIBC approach.
CleanStart images use the same C library (GLIBC), the same system call interface, and the same shared library conventions as Debian, Ubuntu, RHEL, Fedora, SUSE, Amazon Linux, Oracle Linux, and every other major GLIBC-based distribution. The only difference is how the packages are assembled — APK subpackages instead of .deb or .rpm files. To your application, this difference is invisible.
What this means in practice:
Your existing GLIBC-based workflow: FROM ubuntu:22.04 # or debian, rhel, fedora, amazonlinux... RUN apt-get install -y python3 libpq5 COPY myapp/ /app CMD ["python3", "/app/main.py"] Equivalent on CleanStart: FROM registry.cleanstart.com/python3:3.12-prod COPY myapp/ /app CMD ["python3", "/app/main.py"] Your Python code doesn't change.Your pip packages don't change.Your C extensions (numpy, psycopg2, etc.) don't change.Your application behavior doesn't change.Specific compatibility guarantees:
Pre-compiled pip wheels built for manylinux (which targets GLIBC) install and run without recompilation. Java JVMs such as OpenJDK, Temurin, and GraalVM use GLIBC threading, so you avoid the thread-safety issues that plague Alpine/musl environments. Node.js native addons compiled with node-gyp against GLIBC link correctly, and Go binaries compiled with CGO_ENABLED=1 on any GLIBC-based system run unmodified. Rust binaries linked against GLIBC (the x86_64-unknown-linux-gnu target) work as-is. LDAP, Kerberos, and NSS modules all function because GLIBC's Name Service Switch is present. DNS resolution follows the same /etc/resolv.conf and /etc/nsswitch.conf behavior as any GLIBC-based distribution, and locale and Unicode support is identical — en_US.UTF-8, CJK character sets, and ICU all work.
What does NOT work on Alpine (musl) but DOES work on CleanStart (GLIBC):
Scenario | Alpine (musl) | CleanStart (GLIBC) |
|---|---|---|
| Fails — wheel targets GLIBC | Works |
Java app with 1000+ threads | Heap corruption risk | Stable |
| No NSS support | Works |
Application using | Returns NULL | Works |
Go binary compiled on GLIBC Linux with CGO | May segfault | Works |
LDAP authentication via PAM | Not supported | Works |
| Different behavior | Same as any GLIBC distro |
The takeaway: you don't need to test for CleanStart compatibility separately from your existing GLIBC-based environment. If your application runs on any GLIBC-based Linux distribution — Debian, Ubuntu, RHEL, Fedora, SUSE, Amazon Linux, Oracle Linux — it runs on CleanStart. The APK package manager is an implementation detail that delivers smaller images — your application never knows it's there.
APK Technical Details
Package Format
An .apk file is a gzip-compressed tar archive containing several components: .SIGN.RSA.*.pub (the package signature using RSA or EdDSA), .PKGINFO (package metadata including name, version, and dependencies), .trigger (optional post-install trigger scripts), and data.tar.gz (the actual file contents including binaries in usr/bin/, shared libraries in usr/lib/, and configuration files in etc/).
Dependency Resolution
APK uses a SAT solver for dependency resolution — it finds the optimal set of packages that satisfies all constraints:
$ apk add postgresql16 Resolving dependencies: postgresql16 depends on: libpq = 16.1-r0 → installs postgresql16-libs libssl3 >= 3.1 → installs libssl3 subpackage (NOT full openssl) libcrypto3 >= 3.1 → installs libcrypto3 subpackage musl >= 1.2 OR glibc >= 2.38 → satisfied by base libxml2 >= 2.11 → installs libxml2 (NOT libxml2-dev) icu-libs >= 73 → installs icu-libs (NOT icu-dev, NOT icu-doc) Total resolved: 6 runtime subpackagesNOT installed: 14 -dev, -doc, -dbg, -static subpackagesThe solver ensures that only runtime subpackages are pulled in. Development headers, documentation, and debug symbols are never installed unless explicitly requested.
Repository Structure
APK repositories contain an index (APKINDEX.tar.gz) that lists all available packages and their metadata. The index includes entries for each package variant, such as postgresql16-16.1-r0.apk (which depends on libpq, libssl3, libcrypto3, and icu-libs, provides postgresql16=16.1-r0, and is 4218880 bytes), postgresql16-dev-16.1-r0.apk (which depends on postgresql16, libpq-dev, and openssl-dev, provides postgresql16-dev=16.1-r0, and is 892416 bytes), and postgresql16-doc-16.1-r0.apk (which depends on postgresql16, provides postgresql16-doc=16.1-r0, and is 2154496 bytes).
CleanStart maintains its own APK repository with GLIBC-compiled packages, signed with CleanStart's package signing keys.
Comparing Package Counts
To illustrate the granularity difference, here's how many packages or subpackages exist for common software:
Software | dpkg packages | APK subpackages | Granularity ratio |
|---|---|---|---|
OpenSSL 3.1 | 3 | 9 | 3x |
PostgreSQL 16 | 8 | 14 | 1.75x |
Nginx 1.25 | 4 | 12 | 3x |
Python 3.12 | 6 | 11 | 1.8x |
Node.js 20 | 2 | 5 | 2.5x |
GCC 13 | 12 | 22 | 1.8x |
ICU 73 | 4 | 8 | 2x |
curl 8.5 | 3 | 7 | 2.3x |
The key insight here is that APK consistently provides significantly more subpackages than dpkg. More subpackages mean more granular choices about what to include, which directly translates to smaller production images.
The Bottom Line
APK is fundamentally a package manager format, not an operating system in itself. It can be used with any C library implementation, including GLIBC. Subpackages are the critical differentiator that sets APK apart from other package managers. APK's ability to split a single source package into many fine-grained subpackages is the core technology enabling CleanStart's minimal images. CleanStart specifically chose APK for its superior strip-down capability, as no other package manager provides the granularity needed to build images containing only the exact files a running application requires.
CleanStart deliberately chose GLIBC as its C library to ensure broad compatibility. The combination of APK's packaging granularity with GLIBC's enterprise compatibility provides the best of both worlds—small images that work everywhere. The -dev, -doc, and -dbg naming convention is systematic and predictable across APK packages. Every unnecessary component has its own subpackage, making exclusion predictable and automatable through scripts and policies.
What to Read Next
To understand why CleanStart chose GLIBC over musl for production, read GLIBC vs musl. For the minimal container philosophy, see What is Distroless?. Learn about eliminating unnecessary dependencies at the application level in Transitive Dependency Removal, and compare real size numbers across vendors in Image Size Comparison. For the full picture of CleanStart's approach, read How CleanStart is Different, and to understand how -dev subpackages stay in the build stage, see the Builder Pattern guide.
