Docker-based build environment for linuxmuster.net Debian packages.
The image can be used both as a container in a GitHub Actions workflow and as a local build system on a Linux machine.
- Prerequisites
- Directory Structure
- Configuration
- Building the Image
- Local Usage
- Usage as a GitHub Actions Runner
- Release Workflows
- Per-Project Build Scripts
- Publishing the Image (GHCR)
For local use the following are required:
- Docker
bash,curl,python3(forcollect-deps.shandbuild.sh)- Internet access (GitHub API, Ubuntu package repositories)
For GitHub Actions use no local prerequisites are needed — the image is pulled from GHCR.
lmndev-runner/
├── config # Central configuration
├── Dockerfile # Image definition
├── build.sh # Local image build
├── collect-deps.sh # Collects build dependencies, downloads linuxmuster-common
├── start.sh # Starts the container interactively
├── build/ # Per-project build scripts (installed in image at /opt/lmndev/build/)
│ ├── common.sh # Shared build functions
│ ├── linuxmuster-api.sh
│ ├── linuxmuster-base7.sh
│ ├── linuxmuster-cli7.sh
│ ├── linuxmuster-common.sh
│ ├── linuxmuster-fileserver.sh
│ ├── linuxmuster-linbo7.sh
│ ├── linuxmuster-linbo-gui.sh
│ ├── linuxmuster-linuxclient7.sh
│ ├── linuxmuster-prepare.sh
│ ├── linuxmuster-tools.sh
│ ├── linuxmuster-webui7.sh
│ └── sophomorix4.sh
├── workflows/ # Sample release workflows for project repositories
│ ├── linuxmuster-api.release.yml
│ ├── linuxmuster-base7.release.yml
│ └── ... # one .release.yml per project
└── .github/workflows/
└── build-push.yml # Builds and pushes the image itself to GHCR
All settings are located in the config file. It is sourced by build.sh,
collect-deps.sh, and start.sh.
# Ubuntu versions
UBUNTU_VERSION="26.04" # primary version (also receives the :latest tag)
UBUNTU_FALLBACK="24.04" # fallback if the primary version is not available
UBUNTU_VERSIONS="24.04 26.04" # all supported versions (used by ./build.sh all)
# Docker image names
IMAGE_NAME="lmndev-runner"
GHCR_IMAGE="ghcr.io/linuxmuster/lmndev-runner"
# Default shell in the container: bash | zsh | ash | fish
DEFAULT_SHELL="bash"
# Additional packages for local use only (leave empty for GitHub Actions)
EXTRA_PACKAGES=""
# Build user in the container
MY_USER="build"
MY_UID="1000"
MY_GID="1000"
# Supported projects (space-separated)
PROJECTS="linuxmuster-api linuxmuster-base7 ..."| Parameter | Description | Default |
|---|---|---|
UBUNTU_VERSION |
Primary Ubuntu version (also tagged as :latest) |
26.04 |
UBUNTU_FALLBACK |
Fallback version if primary is unavailable | 24.04 |
UBUNTU_VERSIONS |
All versions built by ./build.sh all |
24.04 26.04 |
DEFAULT_SHELL |
Default shell when starting start.sh |
bash |
EXTRA_PACKAGES |
Overrides the Dockerfile's default extra packages | (empty = Dockerfile defaults are used) |
MY_UID / MY_GID |
UID/GID of the build user | 1000 / 1000 |
PROJECTS |
Projects to support | all 12 projects |
The Dockerfile already ships a sensible default set of extra packages:
openssh-client iputils-ping net-tools wget bash bash-completion zsh
zsh-autosuggestions zsh-syntax-highlighting ccache distcc curl
dpkg-dev debdelta sudo vim git
When EXTRA_PACKAGES is empty in config, these Dockerfile defaults are used.
When EXTRA_PACKAGES is set in config, it replaces the Dockerfile defaults entirely.
All four shells are always installed in the image — regardless of configuration. The desired shell is selected at runtime only.
| Value | Package | Shell Path |
|---|---|---|
bash |
bash |
/bin/bash |
zsh |
zsh |
/bin/zsh |
ash |
busybox |
/bin/ash |
fish |
fish |
/usr/bin/fish |
To support only specific projects, shorten the PROJECTS variable in config
accordingly. Build dependencies will then only be collected for the listed projects,
reducing the image size.
Images are built per Ubuntu version and tagged accordingly: :24.04, :26.04.
The primary version (currently 26.04) additionally receives the :latest tag.
./build.sh./build.sh 24.04
./build.sh 26.04./build.sh allcollect-deps.sh is called only once and its output is shared across all versions.
If deps.txt and linuxmuster-common.deb are already present (e.g. after a failed
build), the download step can be skipped:
./build.sh --no-collect
./build.sh --no-collect 24.04
./build.sh --no-collect all- Determine versions to build from the argument (
24.04,26.04,all, or default). - Call
collect-deps.sh— fetches thedebian/controlfile for each configured project from GitHub and extracts allBuild-Dependsfields. The deduplicated package list is written todeps.txt. - Download the latest
linuxmuster-common— the most recent.debpackage is determined via the GitHub Releases API and saved locally. - Run
docker buildfor each version — the image is tagged asIMAGE_NAME:VERSION(and additionally as:latestfor the primary version). Temporary files are deleted afterwards.
The script downloads the debian/control file for each project listed in PROJECTS
directly from GitHub:
https://raw.githubusercontent.com/linuxmuster/PROJECT/main/debian/control
All package names are extracted from the Build-Depends field. During this process:
- Version constraints (e.g.
debhelper (>= 10)) are stripped - Alternatives (
package-a | package-b) are reduced to the first option linuxmuster-*packages are excluded (installed separately)- Duplicates are removed
The linuxmuster-common package is fetched separately via the GitHub Releases API
and installed with gdebi to automatically resolve its dependencies.
./start.sh [source-directory] [shell] [home-directory] [ubuntu-version]| Argument | Description | Default |
|---|---|---|
source-directory |
Mounted as /workspace |
current directory |
shell |
Shell in the container: bash, zsh, ash, fish |
DEFAULT_SHELL from config |
home-directory |
Host directory mounted as /home/build |
none |
ubuntu-version |
Image tag to use: 24.04, 26.04, latest |
UBUNTU_TAG env var or latest |
The shell can also be set via the DEFAULT_SHELL environment variable.
The home directory can alternatively be passed via BUILD_HOME.
The Ubuntu version can alternatively be passed via the UBUNTU_TAG environment variable.
Examples:
# Simple start with default shell and latest image
cd ~/src/linuxmuster-base7
/path/to/lmndev-runner/start.sh .
# Use the Ubuntu 24.04 image
/path/to/lmndev-runner/start.sh ~/src/linuxmuster-base7 bash "" 24.04
# Ubuntu version via environment variable
UBUNTU_TAG=24.04 /path/to/lmndev-runner/start.sh ~/src/linuxmuster-base7
# With a custom home directory and specific Ubuntu version
/path/to/lmndev-runner/start.sh ~/src/linuxmuster-base7 zsh ~/lmndev-home 24.04
# Home directory via environment variable
BUILD_HOME=~/lmndev-home /path/to/lmndev-runner/start.sh ~/src/linuxmuster-base7# Inside the container:
linuxmuster-base7.shNote: If there is an executable script buildpackage.sh in the root directory of
the package source, then this script will be used to build the package.
# Use the default (latest) image
docker run --rm \
-v "$PWD:/workspace" \
-w /workspace \
lmndev-runner:latest \
/opt/lmndev/build/linuxmuster-base7.sh
# Use the Ubuntu 24.04 image explicitly
docker run --rm \
-v "$PWD:/workspace" \
-w /workspace \
lmndev-runner:24.04 \
/opt/lmndev/build/linuxmuster-base7.shThe environment variable OUTPUT_DIR causes the generated .deb files to be moved
to a specific directory:
docker run --rm \
-v "$PWD:/workspace" \
-v "$HOME/packages:/output" \
-w /workspace \
-e OUTPUT_DIR=/output \
lmndev-runner:latest \
/opt/lmndev/build/linuxmuster-base7.shThe image can be used as a job container in any GitHub Actions workflow. All build dependencies of the supported projects are already included in the image.
Images are available with version-specific tags:
| Tag | Ubuntu version | Use for |
|---|---|---|
:latest |
26.04 (primary) | lmn74 and newer distributions |
:26.04 |
26.04 | lmn74 and newer distributions |
:24.04 |
24.04 | lmn73 and older distributions |
jobs:
build:
runs-on: ubuntu-latest
container:
image: ghcr.io/linuxmuster/lmndev-runner:latest
options: --user root
steps:
- uses: actions/checkout@v4
- run: /opt/lmndev/build/linuxmuster-base7.sh
env:
OUTPUT_DIR: ${{ github.workspace }}/packagesThe build scripts are installed in the container at /opt/lmndev/build/ and can be
called directly by name via $PATH.
For each supported project a ready-to-use sample file PROJECT.release.yml is
provided in the workflows/ directory. It can be copied directly into the respective
project repository and used as a GitHub Actions release workflow.
-
Copy the matching file from
workflows/into the project repository:cp workflows/linuxmuster-base7.release.yml \ ~/src/linuxmuster-base7/.github/workflows/release.yml -
Add three secrets to the project repository (
Settings → Secrets and variables → Actions):Secret Description REPO_HOSTHostname of the APT repository server REPO_USERSSH username on the server REPO_SSH_KEYPrivate SSH key (without passphrase) -
Push a release tag — the workflow starts automatically:
git tag v7.3.5-0 git push origin v7.3.5-0
The workflow consists of four sequential jobs:
resolve ──→ build ──→ release ──→ publish
| Job | Description |
|---|---|
| resolve | Reads the target distribution from debian/changelog and selects the matching runner image: lmn74 and higher → :latest (Ubuntu 26.04), all others → :24.04. |
| build | Builds the package inside the lmndev-runner container selected by resolve. Version and distribution are read automatically from the first line of debian/changelog (e.g. linuxmuster-base7 (7.3.5-0) lmn73; urgency=medium). |
| release | Creates a GitHub Release with the generated .deb files. |
| publish | Transfers the packages to the APT repository server via SSH and updates the repository index (repo-update lmn73 or repo-update lmn74). |
The target distribution (lmn73 or lmn74) and the runner image are determined
automatically from debian/changelog — no manual selection is required.
The workflow triggers on tags matching the pattern v[0-9]*, for example:
v7.3.5-0
v7.4.0-1
Each project has its own build script at build/PROJECT.sh. Inside the container
these are installed at /opt/lmndev/build/ and can be called directly by name.
All scripts use the shared build_package() function from build/common.sh.
| Variable | Description | Default |
|---|---|---|
WORKSPACE |
Source directory | $GITHUB_WORKSPACE or current directory |
OUTPUT_DIR |
Target directory for .deb files |
(unset, files remain in parent directory) |
BUILD_FLAGS |
Additional dpkg-buildpackage flags |
(empty) |
CCACHE_DIR |
ccache directory | /tmp/ccache |
ccache is activated automatically when available in the container.
The Linbo build is very resource-intensive (kernel, network drivers, Busybox). The
script build/linuxmuster-linbo7.sh automatically sets:
MAKEFLAGS="-j$(nproc)"
DEB_BUILD_OPTIONS="parallel=$(nproc)"The included workflow .github/workflows/build-push.yml automatically builds all
images on every push to main and pushes them to GHCR:
ghcr.io/linuxmuster/lmndev-runner:24.04
ghcr.io/linuxmuster/lmndev-runner:26.04
ghcr.io/linuxmuster/lmndev-runner:latest ← alias for the primary version (26.04)
In the lmndev-runner repository under Settings → Actions → General, the following
must be set:
Workflow permissions: Read and write permissions
This allows the workflow to write the image to the GitHub Container Registry.
# Build all versions locally
./build.sh all
# Push manually (after gh auth login or docker login ghcr.io)
docker push ghcr.io/linuxmuster/lmndev-runner:24.04
docker push ghcr.io/linuxmuster/lmndev-runner:26.04
docker push ghcr.io/linuxmuster/lmndev-runner:latestThis project is part of the linuxmuster.net development environment.