The Docker images used to build and deploy OpenModelica with Jenkins.
Images are published to:
ghcr.io/openmodelica/build-deps(GitHub Container Registry)docker.openmodelica.org/build-deps(Nexus)
Every image lives on main, keyed by operating system and OS version rather
than by OpenModelica version. Each image is a base plus optional, layered
add-ons, so the heavy common tooling is built once and reused.
main
├── apt/
│ └── Dockerfile # multi-stage: all Ubuntu + Debian versions + add-ons
├── apk/
│ └── Dockerfile # multi-stage: Alpine Linux + add-ons
├── rpm/
│ └── Dockerfile # multi-stage: AlmaLinux, RHEL, Fedora
├── pacman/
│ └── Dockerfile # placeholder (not implemented yet)
└── .ci/
├── matrix.yml # source of truth: which images exist
├── matrix.py # matrix.yml -> CI matrix / tag lookup
├── build.sh # build one image (base + add-ons), write GHA cache
└── publish.sh # restore GHA cache, push + sign one image
- Base image — one per OS/OS-version. Contains everything needed to build OpenModelica (distro packages + common tooling: TeX, Qt, Python venv, ccache, …). This is what most CI jobs use.
- Add-on image — the base plus one thing the distro package manager can't
provide or that needs a pinned version (e.g. CMake 4). Realized as an extra
build stage (
FROMthe base stage) in the same Dockerfile, so shared layers are reused from cache.
Ubuntu and Debian share apt/Dockerfile. The DISTRO and
VERSION build-args select the base image; the Qt package set is picked from
${ID}:${VERSION_ID} at build time. The base image is the full stage; each
add-on is a further stage (e.g. --target cmake-4).
Each image's context, dockerfile, target, build_args and addons
(add-on stage names) are declared in .ci/matrix.yml.
To add a new image, create or extend the OS's Dockerfile and list it in .ci/matrix.yml (a new version of an existing OS needs only a matrix entry).
One image repository per registry; OS, version and variant are encoded in the tag:
| Tag | Mutable? | Meaning |
|---|---|---|
ubuntu-24.04 |
moving | Latest base image for Ubuntu 24.04 |
ubuntu-24.04-2.1.0 |
immutable | Pinned base, synthesized from git tag v2.1.0 |
ubuntu-24.04-cmake-4 |
moving | Latest CMake 4 add-on on the 24.04 base |
ubuntu-24.04-cmake-4-2.1.0 |
immutable | Pinned add-on, synthesized from git tag v2.1.0 |
Releasing is done by pushing a single repo-wide git tag v<MAJOR>.<MINOR>.<PATCH>
(e.g. v2.1.0). CI synthesizes the per-image immutable Docker tags from it and
publishes all images in one run. Day-to-day CI uses the moving tag; when an
OpenModelica release needs a frozen environment it pins the immutable tag.
| OS / version | Base tag | Add-ons | Dockerfile |
|---|---|---|---|
| Ubuntu 26.04 (Resolute) | ubuntu-26.04 |
rust, debug, omsimulator |
apt/Dockerfile |
| Ubuntu 24.04 (Noble) | ubuntu-24.04 |
cmake-4, debug, omsimulator |
apt/Dockerfile |
| Ubuntu 22.04 (Jammy) | ubuntu-22.04 |
debug, omsimulator |
apt/Dockerfile |
| Debian 13 (Trixie) | debian-13 |
cmake-4, debug |
apt/Dockerfile |
| Debian 12 (Bookworm) | debian-12 |
cmake-4, debug |
apt/Dockerfile |
| Alpine 3.24 | alpine-3.24 |
omsimulator |
apk/Dockerfile |
| AlmaLinux 10 | almalinux-10 |
– | rpm/Dockerfile |
| AlmaLinux 9 | almalinux-9 |
– | rpm/Dockerfile |
| Fedora 44 | fedora-44 |
– | rpm/Dockerfile |
| Fedora 43 | fedora-43 |
– | rpm/Dockerfile |
Base image — pick the distro and version with DISTRO and VERSION:
# Ubuntu
docker build --pull --no-cache \
--target full \
--build-arg DISTRO=ubuntu --build-arg VERSION=24.04 \
--tag build-deps:ubuntu-24.04 \
apt
# Debian
docker build --pull --no-cache \
--target full \
--build-arg DISTRO=debian --build-arg VERSION=13 --build-arg INTEL_OCL_PKGS= \
--tag build-deps:debian-13 \
aptAdd-on image — build the add-on's stage with --target. It reuses the
base's cached layers, so it only adds the extra step:
docker build --pull \
--target cmake-4 \
--build-arg DISTRO=ubuntu --build-arg VERSION=24.04 \
--tag build-deps:ubuntu-24.04-cmake-4 \
aptAlpine uses the separate apk/Dockerfile context. Alpine has
no OpenModelica apk repo, so the build dependencies are installed directly; the
only build-arg is VERSION (the Alpine release):
# Base image (the `full` stage)
docker build --pull --no-cache \
--target full \
--build-arg VERSION=3.24 \
--tag build-deps:alpine-3.24 \
apk
# OMSimulator add-on (reuses the base's cached layers)
docker build --pull \
--target omsimulator \
--build-arg VERSION=3.24 \
--tag build-deps:alpine-3.24-omsimulator \
apkEnterprise Linux (AlmaLinux, RHEL) and Fedora share
rpm/Dockerfile. DISTRO and VERSION select the base image;
EPEL/CRB, Qt5/Qt6, autoconf 2.7x and gcc-toolset are picked from ID/PLATFORM_ID
at build time:
# AlmaLinux
docker build --pull --no-cache \
--target full \
--build-arg DISTRO=almalinux --build-arg VERSION=10 \
--tag build-deps:almalinux-10 \
rpm
# Fedora
docker build --pull --no-cache \
--target full \
--build-arg DISTRO=fedora --build-arg VERSION=44 \
--tag build-deps:fedora-44 \
rpmThe values to pass (
context,--file,--target,--build-arg) for any image are exactly its fields in .ci/matrix.yml.
A single workflow, build.yml, runs the whole pipeline so
that a release it creates can publish in the same run (a release created
with GITHUB_TOKEN cannot trigger a separate workflow):
discover ─▶ build (all images, no push)
└─▶ release (tag only) ─▶ publish-ghcr + publish-nexus
- build — on every push/PR to
main(and as the gate before release), builds every base + add-on declared in.ci/matrix.yml(no push). - release — on an repo-wide release tag, creates/updates the GitHub Release.
- publish-ghcr / publish-nexus — build and push the images to GHCR and
Nexus. Moving tags (
<os>-<version>) are updated on every push tomain, the weekly schedule, andworkflow_dispatch. Immutable tags (<os>-<version>-<semver>) are pushed on a release tag, and can also be republished viaworkflow_dispatchwhen a globalv<semver>is supplied. On GHCR the immutable tags are cosign-signed — only on releases (and optionally onworkflow_dispatchre-publishes of a release); the moving tag of a released image points at the same digest, so it verifies with the same signature.
A second workflow, cleanup.yml, deletes
stale versions of the GHCR package: untagged digests left behind by re-pushed
moving tags and orphaned cosign sha256-* signature tags whose image is gone.
It keeps all tagged images, anything still referenced by them, and signatures
of existing images. It runs automatically after every successful GHCR publish
and can also be triggered manually, where it defaults to a dry run — inspect
the log, then re-run it with dry-run unchecked. Note that superseded digests
are pruned right after each publish, so pulling a moving tag's previous image
by digest is not supported.
See RELEASING.md for the step-by-step process.
The original Dockerfile was taken from OpenModelica/OpenModelicaBuildScripts. See LICENSE.md.