Build OCI images
cpak consumes standard OCI images. Include the application, its runtime libraries, declared desktop files, and required assets. The cpak runtime stays on the host.
Use multiple stages
Compile or unpack software in a builder stage, then copy the runtime result into a clean final stage:
FROM golang:1.26-bookworm AS build
WORKDIR /src
COPY . .
RUN CGO_ENABLED=0 go build -trimpath -o /out/example ./cmd/example
FROM debian:13-slim
COPY --from=build /out/example /usr/bin/example
ENTRYPOINT ["/usr/bin/example"]Build tools and package manager caches remain outside the final image. This matters even when layers are shared because every unique byte still has to be downloaded and stored once.
Publish with GitHub Actions
Official Containerpak packages build images in GitHub Actions. A typical workflow publishes amd64 and arm64, uses the GitHub Actions cache, and attaches provenance and an SBOM:
name: Publish
on:
push:
branches: [main]
paths:
- Containerfile
- cpak.json
- .github/workflows/publish.yml
permissions:
contents: read
packages: write
attestations: write
id-token: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/setup-qemu-action@v3
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/build-push-action@v6
with:
context: .
file: Containerfile
platforms: linux/amd64,linux/arm64
push: true
pull: true
tags: ghcr.io/example/example:main
cache-from: type=gha
cache-to: type=gha,mode=max
provenance: mode=max
sbom: trueAdd application-specific checks after the build. Verify every architecture that the workflow claims to publish.
Keep pull: true when the final image follows a platform tag. This makes the build resolve the current platform digest even when the GitHub Actions cache contains an older base.
Base images
Choose a maintained base that supplies the ABI and runtime packages your application expects. The Containerpak images and wine repositories provide reusable environments for official packages with shared needs. Application payloads stay in their package images.
Keep the distribution release explicit. A floating distribution tag can replace libraries without a corresponding package review.
| Image | Base distribution | Intended use | Recipe |
|---|---|---|---|
ghcr.io/containerpak/foundation:main | Ubuntu 26.04 | Pinned, single-layer Ubuntu foundation with certificates and the cpak APT policy | platform/foundation |
ghcr.io/containerpak/base:main | Ubuntu 26.04 | Compatibility name for the foundation | platform/base |
ghcr.io/containerpak/locales:main | Ubuntu 26.04 | Compiled locale data selected by cpak at installation time; not an application base | platform/locales |
ghcr.io/containerpak/mesa64:main | Ubuntu 26.04 | 64-bit OpenGL, Vulkan, Wayland, and common font runtime | platform/mesa64 |
ghcr.io/containerpak/mesa-multilib:main | Ubuntu 26.04 | mesa64 with 32-bit graphics libraries | platform/mesa-multilib |
ghcr.io/containerpak/mesa:main | Ubuntu 26.04 | Multilib graphics runtime with Mesa and Vulkan command-line tools | platform/mesa |
ghcr.io/containerpak/gtk3:main | Ubuntu 26.04 | GTK 3 desktop applications with audio and 64-bit graphics | platform/gtk3 |
ghcr.io/containerpak/webkitgtk:main | Ubuntu 26.04 | GTK 3 applications that use WebKitGTK 4.1 | platform/webkitgtk |
ghcr.io/containerpak/gtk4:main | Ubuntu 26.04 | GTK 4 desktop applications | platform/gtk4 |
ghcr.io/containerpak/adwaita:main | Ubuntu 26.04 | GTK 4 applications that use libadwaita | platform/adwaita |
ghcr.io/containerpak/webkitgtk6:main | Ubuntu 26.04 | GTK 4 and libadwaita applications that use WebKitGTK 6 | platform/webkitgtk6 |
ghcr.io/containerpak/desktop:main | Ubuntu 26.04 | Complete desktop runtime for applications that require GTK 3, GTK 4, libadwaita, and WebKitGTK 4.1 | platform/desktop |
ghcr.io/containerpak/gtk:main | Ubuntu 26.04 | Compatibility name for desktop; new packages should select a versioned GTK branch | platform/gtk |
ghcr.io/containerpak/wine:main | Ubuntu 26.04 | Host libraries for application images that supply Wine or Proton, including 32-bit graphics, audio, input, and multimedia | Containerpak/wine |
SDK images follow the same split:
| Image | Intended use | Recipe |
|---|---|---|
ghcr.io/containerpak/base-sdk:main | General C and C++ build environment | sdk/base |
ghcr.io/containerpak/mesa64-sdk:main | 64-bit graphics headers and build tools | sdk/mesa64 |
ghcr.io/containerpak/mesa-sdk:main | Multilib graphics headers and tools | sdk/mesa |
ghcr.io/containerpak/gtk3-sdk:main | GTK 3 development headers | sdk/gtk3 |
ghcr.io/containerpak/webkitgtk-sdk:main | GTK 3 and WebKitGTK 4.1 development headers | sdk/webkitgtk |
ghcr.io/containerpak/gtk4-sdk:main | GTK 4 and libadwaita development headers | sdk/gtk4 |
ghcr.io/containerpak/desktop-sdk:main | Complete SDK for GTK 3, GTK 4, libadwaita, and WebKitGTK 4.1 | sdk/desktop |
ghcr.io/containerpak/gtk-sdk:main | Compatibility name for desktop-sdk | sdk/gtk |
The main tag follows the current platform build. ubuntu-26.04 follows the current build for that Ubuntu release. Tags such as ubuntu-26.04.20260814.3 and sha-<revision> identify one published platform state and are suitable for reproducible builds.
Official platform images identify the matching locale image in their OCI configuration. cpak reads the user's locale, imports only the required compiled directories, and adds the resulting shared layer to the application. Packages retain their own translation catalogs and do not need to include locales-all.
The distribution choice defines the ABI and library versions available to the application. Pick the smallest base that already matches the software, pin the final application image by digest through cpak, and review base updates in CI before publishing them.
Install additional runtime libraries and clean APT in the same layer:
FROM ghcr.io/containerpak/gtk3:ubuntu-26.04
RUN apt-get update && \
apt-get install -y libexample1 && \
cpak-clean-junkThe platform APT policy already disables recommended and suggested packages, downloaded package retention, manuals, package reports, and build documentation. Do not replace that policy or install locales-all in each application image. Keep application translation catalogs in the package; cpak supplies the compiled host locale through the shared locale layer.
Layer layout
Group stable runtime content before frequently changing application content. OCI registries and cpak address layers by digest, so unchanged base layers can be shared by many packages and retained across updates.
Avoid a single giant RUN step when it causes an application update to invalidate unrelated runtime content. Avoid many tiny layers that exist only to mirror individual shell commands. Split at boundaries that are likely to change independently.
Design for two-level deduplication
Shared base images are useful beyond build consistency. cpak stores OCI layers by digest, so applications built on the same unchanged base reuse one downloaded and stored layer. A new layer is unpacked only when its digest is absent.
cpak streams a new layer directly into the global FVS content store. FVS splits file contents into content-defined blocks and reuses blocks already referenced by another layer. This catches equal ranges even when two images placed them in different files or produced different OCI digests.
OCI digest match -> reuse the complete layer
New OCI layer -> verify and decode as one stream
FVS block match -> reference the existing content block
Unique block -> store one new content blockThe first level reuses stable bases and matching layer boundaries. The second level finds repeated content across different layer layouts. Both run automatically during image pull. A successful import retains the FVS representation used by installed packages.
For registries and CDNs that preserve byte-range responses, zstd:chunked lets cpak read the layer table of contents first and skip compressed file ranges whose complete content already exists in FVS. cpak uses one complete stream for a cold store and switches to ranges only when known content makes that cheaper. A normal gzip or zstd pull remains the automatic fallback. Read Choose and operate an OCI registry before enabling it in CI.
External artifacts
Prefer downloading build inputs in CI and checking their vendor-provided checksum before use. If an artifact must be installed on the user's machine, declare it through runtime_sources with its HTTPS URL, exact size, SHA-256, and a tar, dpkg, deb-extract, rpm, or file installer. This is also the correct boundary when the vendor permits users to download an application but does not permit the Store to republish its payload. See Runtime sources for the archive and file contracts and CI checks.
Verify the result
After the image is published:
cpak validate cpak.json
cpak lock cpak.json
cpak test cpak.jsonRun at least one declared binary. Desktop packages also need a real visual launch through cpak on each supported display path.
