The basics of using the build system are in BUILD.md. It is highly recommended you start there. This document describes how to update eve.
Alpine Linux is the base
packaging system used for software dependencies in eve, and its
apk packaging system is the base for binary artifacts.
A base image lfedge/eve-alpine is composed from the source in
pkg/alpine and is used as the FROM image for all
other packages in pkg/.
As long as eve-alpine already contains the packages you need, you simply reference it in your Dockerfile:
FROM lfedge/eve-alpine:<hash>For example, as of this writing, the uefi Dockerfile begins:
FROM lfedge/eve-alpine:9fb9b9cbf7d90066a70e4704d04a6fe248ff52bb as buildIf you need to add a package to eve-alpine, you need to update eve-alpine itself.
Alpine's apk packaging system is small, lightweight, and efficient.
It also has an excellent dependency resolution graph, enabling you to
require just one package, and have it determine all additional dependencies
and their install order.
apk's shortcoming is in the inconsistencies of installs.
First, installing a package, e.g. apk install curl, does not guarantee
any specific version. It can and will take the latest version available.
This makes it difficult to impossible to provide reproducible builds.
Second, even if you pin to a specific version, Alpine's apk maintainers
reserver the right to update the bits behind a specific package behind
the covers, normally for bugfixes or security concerns. That means that
even if you install curl=8.1.0-r0, and the next time install the exact same version, you still might get changed bits.
Third, and this is not unique to Alpine, every time you install packages,
in the case of Alpine via apk add, you are downloading packages from the
Internet. This makes every eve package that needs, well, packages dependent
on downloading over and over again.
For this reason, eve-alpine exists. Its purpose is to be a consistent cache.
- cache: it already has all of the desired packages (
.apkfiles) installed - consistent: because it already has all of the desired packages installed, you are guaranteed to get the exact same version and bits with each install
For this reason, every downstream package in pkg/, except for
pkg/alpine itself, of course, must take all of its packages from
eve-alpine and not plain old alpine.
Or, in other words:
# ALWAYS do this:
FROM lfedge/eve-alpine:<hash>
# NEVER do this:
FROM alpine:3.17 # or any other versionSo long as your specific version of eve-alpine has the packages you need,
do the above, and you are done. apk add will provide it for you.
What do you do if you need new packages? Or if you need to update eve-alpine
itself to be based on a new version of Alpine?
The process of adding new packages to eve-alpine and using them is:
- Update pkg/alpine/Dockerfile to depend on the current cache
- Update the list of packages in pkg/alpine/mirrors/ to add the new package(s)
- Commit your changes
- Rebuild
eve-alpine - Update your downstream dependency to consume the new version of
eve-alpine
As described above, eve-alpine itself is a cache. To be a successful cache, we don't want to download all of our packages in the cache, either, just because we added one new package. Instead, we want to add the new package alone, and leave the rest of the cache untouched.
To do this, we rely on previous versions of eve-alpine. Thus, we do
not build the cache this way:
FROM alpine:3.17
RUN apk add \
acl \
acpi \
alpine-baselayout \
alpine-keys \
apk-tools \
argp-standalone \
attr-dev \
autoconf \
...Doing so would reinstall all of the packages from scratch each build,
and could lead to changed versions and bits.
Instead, we use the previous version. Let's say we last finished building
eve-alpine, and the last version was lfedge/eve-alpine:269db59bde11b9acd9a5485c98a7c1383a159613.
Then the next time we need to add a package, we update the FROM line to be
our previous build.
# change this line from what it was before
FROM lfedge/eve-alpine:269db59bde11b9acd9a5485c98a7c1383a159613Where are those packages listed, so we can add our new ones? They are stored in pkg/alpine/mirrors. In that directory, there is a folder for each Alpine release, allowing you to add packages you wish to pull from specific releases of Alpine, independent of what version your current build is based upon.
Thus, eve-alpine might be based on alpine 3.17, and you could
add a package from 3.16 and from edge, just be creating the directories:
pkg/alpine/mirrors/3.16/pkg/alpine/mirrors/edge/
Inside each such directory is a file per repository - main or community.
Any package listed on its own line in one of those files will be installed
for all eve architectures.
If you want a package just for a specific architecture, you can list if in
a file named main.<arch> or community.<arch>, e.g. main.x86_64.
Look in the existing directories for examples.
You should commit your changes before you go on.
You do not absolutely have to commit your changes for this to work.
However, the tag for your new eve-alpine image is calculated from
the contents of the directory pkg/alpine. If there are
uncommitted changes, then the tag will be different than you expect,
containing the word -dirty and a hash of all of the file contents.
You can check what that is by running linuxkit pkg show-tag pkg/alpine.
For example, as of this writing:
$ linuxkit pkg show-tag pkg/alpine
lfedge/eve-alpine:269db59bde11b9acd9a5485c98a7c1383a159613If I change a file and do not commit it:
$ linuxkit pkg show-tag pkg/alpine
lfedge/eve-alpine:269db59bde11b9acd9a5485c98a7c1383a159613-dirty-564e0f5You never will want to push out uncommitted changes (nor will CI let you),
but you also never want to have the FROM tag in your downstream
Dockerfile have -dirty in it, unless it is just for
local, temporary testing. Thus, commit your changes.
Back in our root directory, we can now rebuild eve-alpine:
make eve-alpineThis, in turn, will build our new eve-alpine, but it also will give
us the new eve-alpine hash, which we can use to update our downstream
packages. For example, if the new one is lfedge/eve-alpine:d57bcaf532853f72033be6f0f573d725b8e9114e, then that is what you use in your downstream package.
FROM lfedge/eve-alpine:d57bcaf532853f72033be6f0f573d725b8e9114eNow you can add those packages in the downstream
The rule of thumb then is:
- Your downstream package
DockerfiledependsFROMlatest version oflfedge/eve-alpine pkg/alpine/DockerfiledependsFROMthe previous version oflfedge/eve-alpine
EVE provides a set of Python tools in tools/alpine-tools/ to help manage Alpine package lists and handle version migrations:
Core library providing shared utilities for APKINDEX parsing, dependency resolution, and package classification. This module is used by the other Alpine tools and provides:
- Download and parse APKINDEX files from Alpine mirrors
- Recursive dependency resolution with cycle detection
- Handle virtual packages (so:, cmd:, pc: prefixes) and metapackages
- Package classification by branch and architecture availability
- Missing dependency reporting
Migrates complete package lists between Alpine versions (e.g., 3.16 → 3.21) or performs same-version migrations to update missing dependencies (e.g., 3.16 → 3.16). This tool is essential when upgrading EVE to a new Alpine base version or refreshing dependencies, as it handles:
- Package relocations between main/community branches
- Renamed or removed packages (reported for manual review)
- Automatic dependency resolution for all existing packages
- Architecture-specific package availability changes
- Same-version dependency updates without changing Alpine version
Usage:
# Migrate from Alpine 3.16 to 3.21
python3 tools/alpine-tools/alpine_migrate.py 3.16 3.21
# Same-version migration to update missing dependencies
python3 tools/alpine-tools/alpine_migrate.py 3.16 3.16
# With dependency chain analysis
python3 tools/alpine-tools/alpine_migrate.py 3.16 3.21 --print-chainsAdds specific packages with full dependency resolution to existing Alpine package lists. Useful for expanding the package cache with new tools or libraries without doing a full migration:
- Adds packages to existing package lists (preserves current packages)
- Automatically resolves and includes all dependencies
- Reports newly added packages for verification
- Supports dependency chain analysis for debugging
Usage:
# Add a single package
python3 tools/alpine-tools/alpine_pkg_add.py 3.21 vim
# Add multiple development tools
python3 tools/alpine-tools/alpine_pkg_add.py 3.21 git curl wget build-base
# Add with dependency chain analysis
python3 tools/alpine-tools/alpine_pkg_add.py 3.21 python3-pip --print-chainsThese tools are particularly useful when:
- Migrating EVE to a new Alpine version where packages have moved between branches
- Updating missing dependencies within the same Alpine version
- Adding new packages to the eve-alpine cache
- Debugging dependency resolution issues
- Understanding why certain packages are included in the cache
The first option requires update of eve-alpine-base image to use another minirootfs and repository and pointing eve-alpine to be based on novel eve-alpine-base using FROM lfedge/eve-alpine-base. This action will invalidate all stored packages inside eve-alpine and download them from repository.
The second option will append package to cache. Unlike a lot of other Linux distributions, Alpine Linux doesn't provide historical versions of all its packages. In fact, Alpine Linux reserves the right to update packages with security patches behind the scenes resulting in content of Alpine x.y.z repositories shifting slightly from time to time. This, obviously, goes against the principle of reproducible builds and makes our lfedge/eve-alpine cache serve a double function: not only it is used to speed up the build, but it also may end up being the only place on the Internet where a certain Alpine Linux package version x.y.z could be available from.
The latter aspect makes maintaining lfedge/eve-alpine a bit tricky, even though at its core it is simply driven by the list of packages recorded in the manifest files under pkg/alpine/mirrors. Removing packages from the cache is not advisable (and should really only be done during major EVE version updates). Adding packages to the cache consists of two steps:
- adding new package names to the right manifest file (under
pkg/alpine/mirrors/<BASE ALPINE VERSION>/[main|community]) - updating
FROM ... AS cacheline in pkg/alpine/Dockerfile to point to the last version of the cache
Step #2 guarantees that all existing packages will simply be reused from the previous version of the cache and NOT re-downloaded from the Alpine http mirrors (remember that re-downloading always runs the risk of getting a different version of the same package). Step #1, of course, will download new packages and pin them in the new version of the cache.
If the above seems a bit confusing, don't despair: here's an actual example of adding 4 new packages libfdt, dtc, dtc-dev and uboot-tools to the lfedge/eve-alpine cache.
graphviz sources:
digraph G {
node [shape=box, color=blue, fontcolor=darkgreen];
edge [label="FROM", fontcolor=red, dir=back];
"lfedge/eve-alpine:9fb9b9cbf7d90066a70e4704d04a6fe248ff52bb" -> "pkg/pillar";
}
digraph G {
node [shape=box, color=blue, fontcolor=darkgreen];
edge [label="FROM", fontcolor=red, dir=back];
"lfedge/eve-alpine:269db59bde11b9acd9a5485c98a7c1383a159613" [xlabel="previous eve-alpine"];
"lfedge/eve-alpine:269db59bde11b9acd9a5485c98a7c1383a159613" -> "lfedge/eve-alpine:d57bcaf532853f72033be6f0f573d725b8e9114e";
"lfedge/eve-alpine:d57bcaf532853f72033be6f0f573d725b8e9114e" [xlabel="current eve-alpine Dockerfile"];
"lfedge/eve-alpine:d57bcaf532853f72033be6f0f573d725b8e9114e" -> "pkg/yourpkg/Dockerfile";
}

