Utility Images: flatpak and flatpak-builder#
Overview#
The aetherpak/flatpak-containers repository publishes two utility container images — ghcr.io/aetherpak/flatpak and ghcr.io/aetherpak/flatpak-builder — that provide a self-contained Flatpak toolchain for building and distributing Flatpak applications .
Both images are built on top of registry.fedoraproject.org/fedora-minimal, with the base digest pinned for reproducibility . This means every build of these images — locally or in CI — starts from exactly the same upstream layer, eliminating "it worked on my machine" drift caused by upstream image updates.
They serve two primary purposes within the AetherPak ecosystem:
-
Foundation for the AetherPak CLI images. The
ghcr.io/aetherpak/cli:<version>andghcr.io/aetherpak/cli:<version>-builderimages are built directly on top of these utility images. Every tagged CLI release inherits its Flatpak toolchain from a specificflatpak-containersbuild . -
Local development of the AetherPak CLI itself. The images can be pulled directly to develop and test the
aetherpakCLI source code in an environment that closely mirrors the AetherPak CI pipeline .
Runtime use of the AetherPak CLI. If you want to run a released version of the
aetherpakCLI in a container, use theghcr.io/aetherpak/cliimages instead — NOT the utility images. Theaetherpak/clicontainers come in two variants: a vanilla variant based onaetherpak/flatpakand a builder image variant based onaetherpak/flatpak-builder. The key distinction: the utility images (flatpakandflatpak-builder) are for CLI development; theaetherpak/clicontainers are for runtime/production use of the CLI.
A key design goal is parity with the Flathub GitHub Actions builder environment. The tool selection — including flatpak-builder-lint pulled directly from the flathub-infra GitHub organisation — is chosen to ensure that linting and build behaviour is consistent whether a package is built locally against these images or in Flathub's own CI .
Supported use cases. These images are designed and maintained for AetherPak's internal needs. While they can be used independently for general Flatpak development workflows, that use case is not explicitly supported.
Available Images#
ghcr.io/aetherpak/flatpak:latest — the base image#
This is the lighter of the two images. It installs the minimum set of tools needed to run most aetherpak commands (import, push-oci, build-site) and provides a pre-configured Flathub remote .
| Property | Value |
|---|---|
| Base | registry.fedoraproject.org/fedora-minimal (pinned by digest) |
| Working directory | /workspace |
| Default command | /bin/bash |
Included packages :
| Package | Purpose |
|---|---|
flatpak | Flatpak runtime and CLI |
ostree | OSTree library and CLI |
ca-certificates | TLS root certificates |
git | Version control (manifest sources, submodules) |
jq | JSON processing |
curl | HTTP downloads |
tar / gzip | Archive utilities |
gnupg2 | GPG signing and verification |
shared-mime-info | MIME type database (required by Flatpak) |
Packages are installed with --setopt=install_weak_deps=0 --nodocs to minimise image size, and the microdnf cache is cleaned immediately after .
The Flathub repository remote is pre-configured during the image build so that runtimes and SDKs can be resolved without any additional setup :
flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
ghcr.io/aetherpak/flatpak-builder:latest — the builder image#
This image extends the base image via a multi-stage build and adds everything required to compile a Flatpak from a manifest . It is the image used whenever flatpak-builder needs to run — for example, during the build-manifest job in the AetherPak reusable workflow.
| Property | Value |
|---|---|
| Base | ghcr.io/aetherpak/flatpak (via --build-arg BASE_IMAGE) |
| Working directory | /workspace (inherited from base) |
| Default command | /bin/bash (inherited from base) |
Additional packages :
| Package | Purpose |
|---|---|
flatpak-builder | Flatpak manifest build tool |
elfutils | ELF binary analysis (debuginfo extraction) |
appstream | AppStream metadata validation |
desktop-file-utils | .desktop file validation |
python3-pip | Python package installer (required for lint tool) |
gobject-introspection | GObject type introspection |
cairo | 2D graphics library (dependency of lint tool) |
patch | Apply source patches from manifests |
unzip / bzip2 / zstd | Additional archive format support |
flatpak-builder-lint is installed from source using pip3 :
pip3 install --no-cache-dir git+https://github.com/flathub-infra/flatpak-builder-lint.git
To compile this tool, a set of build-time dependencies (gcc, pkgconf, cairo-devel, cairo-gobject-devel, gobject-introspection-devel, python3-devel, glib2-devel) are installed, used, and then removed in the same RUN layer to keep the final image size lean .
Architecture Support#
Both images are published as multi-architecture OCI manifests covering linux/amd64 and linux/arm64 . Container runtimes (Docker, Podman) will automatically select the correct variant for the host machine when pulling the manifest-list tag.
Builds run on native runners for each architecture — ubuntu-latest for amd64 and ubuntu-24.04-arm for arm64 — avoiding the performance overhead of emulation . Each architecture produces an architecture-suffixed intermediate tag (sha-{SHORT_SHA}-amd64 / sha-{SHORT_SHA}-arm64). Once both architecture builds succeed, a merge-manifests job uses podman manifest create/add/push to combine them into a single manifest list under the final tag .
Single-architecture tags (e.g. latest-amd64, latest-arm64) are also promoted via skopeo copy for use cases where a specific architecture must be targeted explicitly .
Architecture limitations. Only
x86_64(amd64) andaarch64(arm64) are supported. There is no cross-compilation support; each architecture is built natively .
Relationship to AetherPak CLI Images#
The flatpak-containers images are the foundation of the AetherPak CLI container images. The aetherpak/cli Containerfile defines two build arguments that reference them directly :
ARG BASE_IMAGE=ghcr.io/aetherpak/flatpak:latest
ARG BUILDER_IMAGE=ghcr.io/aetherpak/flatpak-builder:latest
The aetherpak binary is compiled separately (in a golang:1.26-alpine builder stage) and then copied into both images , producing two final stages:
| CLI Image Tag | Containerfile Stage | Base | Contents |
|---|---|---|---|
ghcr.io/aetherpak/cli:<version> | cli | ghcr.io/aetherpak/flatpak | aetherpak + full flatpak toolchain |
ghcr.io/aetherpak/cli:<version>-builder | cli-builder | ghcr.io/aetherpak/flatpak-builder | aetherpak + flatpak-builder + lint tools |
In practice, the AetherPak reusable workflow selects the appropriate CLI image based on the job type :
ghcr.io/aetherpak/cli:<version>is used byplan,prep-bundle,publish-oci, andpublish-sitejobs — steps that only need the Flatpak client, OSTree tooling, and theaetherpakbinary.ghcr.io/aetherpak/cli:<version>-builderis used by thebuild-manifestjob — the step that actually compiles the Flatpak from a manifest usingflatpak-builder.
This split keeps the non-build jobs fast by avoiding the overhead of pulling the heavier builder image when only the base toolchain is required.
Flathub Parity#
A core goal of these images is to closely mirror the GitHub Actions builder environment that Flathub uses for its own CI pipeline. This matters because:
- Consistent lint results. Flatpak manifest linting is done by
flatpak-builder-lint, a tool maintained by theflathub-infraorganisation. Running the same version locally (viaghcr.io/aetherpak/flatpak-builder) ensures that any lint failures you see in the AetherPak CI will also surface when you run checks locally, and vice versa. - Reproducible builds. The package set in the builder image (
elfutils,appstream,desktop-file-utils,gobject-introspection,cairo, etc.) mirrors the compilation toolchain expected by Flatpak manifests published to Flathub. - Pre-configured Flathub remote. The base image adds the Flathub OSTree repository as a remote during the image build . This means
flatpak-buildercan resolve runtimes and SDKs from Flathub without any additional setup step.
The most direct expression of this parity is the flatpak-builder-lint installation. Rather than pulling a versioned release from PyPI, the builder image installs directly from the canonical upstream source :
pip3 install --no-cache-dir git+https://github.com/flathub-infra/flatpak-builder-lint.git
This ensures the lint tool is always sourced from the same repository that Flathub itself maintains, keeping AetherPak's validation behaviour in sync with Flathub's expectations.
Note. While AetherPak aims for parity with Flathub's build environment, the images are not formally tested against Flathub's own builder configuration and may diverge over time. If you are preparing a submission to Flathub, always run a final validation in Flathub's own CI.
Building the Images Locally#
The source repository is at github.com/aetherpak/flatpak-containers. A single Containerfile defines both images using named --target stages: flatpak and flatpak-builder .
To build the images locally, clone the repository and run:
# Build the base image
podman build -t flatpak:local --target flatpak .
# Build the builder image (uses the local flatpak image as its base)
podman build \
-t flatpak-builder:local \
--target flatpak-builder \
--build-arg BASE_IMAGE=flatpak:local \
.
The --build-arg BASE_IMAGE=flatpak:local argument is important: without it, the flatpak-builder stage defaults to the flatpak stage name within the multi-stage build context. Passing an explicit tag lets you point the builder stage at any base image — including a locally modified or custom-tagged variant .
Once built, reference the local tags in a compose.yml to use them in place of the published images:
services:
builder:
image: flatpak-builder:local
volumes:
- .:/workspace
working_dir: /workspace
privileged: true
command: sleep infinity
CI Build Behaviour#
In the published CI workflow, image builds are only triggered when the Containerfile or the CI workflow file itself changes, or when a build is dispatched manually . This avoids unnecessary rebuilds on documentation-only or unrelated code changes.
Usage Outside AetherPak#
The flatpak-containers images are general-purpose Fedora-based Flatpak toolchain images. They can be used independently for any workflow that needs flatpak, flatpak-builder, or the associated tooling without depending on the aetherpak binary. For example, you could use them to run flatpak-builder directly, validate manifests with flatpak-builder-lint, or as a base layer for custom container images.
That said, this use case is not explicitly supported. These images are designed and maintained to serve AetherPak's internal needs: providing a consistent, reproducible base for the CLI images and for local development against the AetherPak CI pipeline . Breaking changes to package versions, image layout, or tag conventions may be made without advance notice if they benefit the AetherPak workflow.
If you do use these images outside of AetherPak, keep the following in mind:
- Privileged mode is required for
flatpak-builderbuilds.flatpak-builderneeds to write to the system Flatpak installation when installing runtimes and SDKs from the pre-configured Flathub remote. In a container environment, the polkit/dbus system helper is not available, so the container must run as root with--privileged. - No cross-compilation. Architecture support is limited to
linux/amd64andlinux/arm64; there is no cross-compilation toolchain included . - Pin a specific digest. The
latesttag moves forward with each push tomain. For reproducible workflows outside AetherPak, pin to an immutable digest or a specific version tag rather thanlatest.