Ubuntu 24.04 Rootfs: Base Image Build and Integration for the Iximiuz Labs¶
Context¶
Ubuntu 24.04 Rootfs is the base image for all SilverStack iximiuz playground machines - every other rootfs in this stack (FROM ghcr.io/ibtisam-iq/ubuntu-24-04-rootfs:latest) builds on top of it.
It is consumed directly by child images such as Dev Machine, Jenkins, Nexus, and SonarQube. Those images assume this base is already systemd-enabled, SSH-ready, and equipped with a curated DevOps toolset.
Important: This image is not a regular Docker application image. It is a microVM rootfs designed for the iximiuz Labs platform, which boots it as a full virtual machine using its own kernel and init system. When the platform boots the VM, systemd becomes PID 1 automatically - the image itself does not need a
CMDorENTRYPOINT. Attempting to validate this image with plaindocker runwill not produce a working systemd environment; see Verification for the correct approach.
The image is defined under:
- README:
iximiuz/rootfs/ubuntu/README.md - Dockerfile:
iximiuz/rootfs/ubuntu/Dockerfile - Scripts:
iximiuz/rootfs/ubuntu/scripts/ - System-wide prompt:
iximiuz/rootfs/ubuntu/configs/profile.d/00-prompt.sh - Welcome banner:
iximiuz/rootfs/ubuntu/welcome - CI workflow:
.github/workflows/build-ubuntu-rootfs.yml
Objectives¶
Ubuntu 24.04 Rootfs must:
- Provide a fully unminimized, systemd-enabled Ubuntu 24.04 environment that behaves like a real server inside iximiuz microVMs.
- Configure SSH with key-based authentication only, optimized for low-latency playground access.
- Deliver a sane default shell environment: custom PS1 prompt, bash completion, vim, fzf, ripgrep, and Git configuration helpers.
- Ship a curated DevOps toolset (arkade, jq/yq/fx, task/just, btop, cfssl, code-server, websocat) that child images can rely on without re-installing.
- Create a consistent non-root interactive user (
$USER, defaultibtisam) with tuned.bashrc,.gitconfig, and.vimrc. - Be built reproducibly via GitHub Actions, published multi-arch to GHCR as
ghcr.io/ibtisam-iq/ubuntu-24-04-rootfs.
Architecture / Conceptual Overview¶
How iximiuz Uses This Image¶
The iximiuz Labs platform boots playground machines by mounting the OCI image as a block device filesystem (rootfs) for a microVM. The platform's own kernel and bootloader handle VM initialization - this is not Docker container execution. This means:
- systemd as PID 1 is guaranteed by the platform's boot process, not by anything inside the image.
- SSH host keys are intentionally absent from the image (deleted during build). The iximiuz platform regenerates them at VM first boot.
- Machine IDs (
/etc/machine-id,/var/lib/dbus/machine-id) are intentionally emptied during build so each VM generates its own unique identity on first boot. /.dockerenvis removed during build to prevent systemd from treating the environment as a container.
What the Image Provides¶
The base rootfs:
- Starts from
ubuntu:24.04and runsunminimizeto restore full userland (man pages, locales, standard tools). - Installs systemd, SSH, and a standard Linux troubleshooting toolkit.
- Cleans container-specific artifacts and empties machine IDs.
- Adds a systemd "examiner" service (via
set-up-systemd-examiner-service.sh) so child images can inspect service state programmatically. - Installs system-wide tools via
get-*scripts (arkade, common CLIs, btop, cfssl, websocat, fx). - Installs user-specific tools and customizations for both
rootand the non-root$USER.
Child images can safely assume:
- systemd is the init system and manages services.
- SSH is configured and enabled (host keys are regenerated by the platform at boot).
- The interactive
$USERexists with a consistent shell experience. - Core utility tools and the examiner service are already present.
Key Design Decisions¶
-
Unminimized Ubuntu -
unminimizeensures man pages, locales, and standard tools are available, which is essential for hands-on labs. -
No
CMDorENTRYPOINTin the image - iximiuz boots the VM with its own kernel. The image must not define an entrypoint. Adding one would conflict with how the platform mounts and boots the rootfs. -
SSH host keys intentionally deleted -
rm -f /etc/ssh/ssh_host_*is done during build so every VM instance gets unique host keys. Thesshd-keygensystemd units are masked to prevent Ubuntu's built-in key generation from running; the iximiuz platform handles key generation at VM boot. -
Machine IDs emptied at build time - ensures each VM generates its own D-Bus and systemd machine identity on first boot.
-
networkd-dispatcher.servicemasked - reduces journald noise for lab environments where dynamic networkd reconfiguration is not needed. -
Script-driven tooling - small focused scripts under
scripts/keep the Dockerfile readable and allow child images to reuse or override individual setup steps. -
Single base for all rootfs images - Dev Machine, Jenkins, Nexus, SonarQube, and future images all build
FROMthis base, ensuring consistent behavior and eliminating duplicated setup.
Source Layout¶
ubuntu/
├── Dockerfile
├── welcome # Welcome banner (copied to $HOME/.welcome)
├── configs/
│ └── profile.d/
│ └── 00-prompt.sh # System-wide PS1 prompt (login shells)
└── scripts/
├── add-user.sh # Creates non-root $USER
├── customize-bashrc.sh # Appends to ~/.bashrc (prompt, welcome, fzf, etc.)
├── customize-git.sh # Sets ~/.gitconfig defaults
├── customize-vimrc.sh # Sets ~/.vimrc defaults
├── get-arkade.sh # Installs arkade
├── get-btop.sh # Installs btop at $BTOP_VERSION
├── get-cfssl.sh # Installs cfssl at $CFSSL_VERSION
├── get-code-server.sh # Installs code-server (VS Code in browser)
├── get-common-tools.sh # Installs jq, yq, task, just, etc.
├── get-fzf.sh # Installs fzf
├── get-websocat.sh # Installs websocat at $WEBSOCAT_VERSION
└── set-up-systemd-examiner-service.sh
Note on
welcomefile placement:customize-bashrc.shappends logic to~/.bashrcthat displays and then permanently deletes~/.welcomeon the first interactive login. TheCOPY welcome $HOME/.welcomeinstruction must therefore appear after allRUNscript steps so the file is not consumed during a non-interactive build layer.
Build Arguments¶
| ARG | Default | Description |
|---|---|---|
USER | ibtisam | Non-root interactive user to create |
BTOP_VERSION | 1.4.4 | btop release version |
CFSSL_VERSION | 1.6.5 | cfssl release version |
WEBSOCAT_VERSION | 1.14.1 | websocat release version |
ARKADE_BIN_DIR | /usr/local/bin | Installation path for arkade |
BUILD_DATE | (set by CI) | OCI label: image creation timestamp |
VCS_REF | (set by CI) | OCI label: git commit SHA |
Prerequisites¶
- Docker with Buildx (for multi-arch builds mirroring CI).
- A local checkout of
github.com/ibtisam-iq/silver-stackwith theiximiuz/rootfs/ubuntutree. - Network access to fetch packages and GitHub releases referenced by the
get-*scripts. - For CI: a GitHub repository with
packages: writepermission to push to GHCR.
Build Steps¶
1. Local Build¶
From the iximiuz/rootfs/ubuntu/ directory:
IMAGE_NAME="ghcr.io/ibtisam-iq/ubuntu-24-04-rootfs:latest"
docker build \
--build-arg USER="ibtisam" \
--build-arg ARKADE_BIN_DIR="/usr/local/bin" \
--build-arg BTOP_VERSION="1.4.4" \
--build-arg CFSSL_VERSION="1.6.5" \
--build-arg WEBSOCAT_VERSION="1.14.1" \
-t "${IMAGE_NAME}" \
.
The Dockerfile performs the following major steps:
Step 1 - Unminimize Ubuntu and install base packages
- Starts from
ubuntu:24.04. - Copies
unminimizefromubuntu:22.04(Ubuntu 24.04 dropped it from its own image). - Installs system packages:
systemd, SSH prerequisites, and a debugging toolkit (curl,htop,mtr,traceroute,nftables,socat, etc.). - Runs
yes | unminimizeto restore the full userland. - Masks
networkd-dispatcher.serviceto reduce journald noise. - Removes
/etc/update-motd.d/*,/.dockerenv, and empties machine IDs for per-VM identity generation. - Sets
rootpassword toroot.
Step 2 - Configure SSH
- Installs
openssh-server. - Appends to
/etc/ssh/sshd_config: key-based auth only (AuthenticationMethods publickey), IPv4-only (AddressFamily inet), DNS disabled (UseDNS no),MaxAuthTries 50,PrintLastLog no. - Masks
sshd-keygen@.serviceandsshd-keygen.target(platform regenerates host keys at VM boot). - Disables socket activation (
ssh.socket), removes socket override files, enablesssh.service. - Deletes all pre-generated host keys (
rm -f /etc/ssh/ssh_host_*) - each VM gets unique keys from the platform.
Step 3 - Systemd examiner service
- Copies
examiner*binaries to/usr/local/bin. - Runs
set-up-systemd-examiner-service.shwhich creates/etc/systemd/system/examiner.serviceand symlinks it intomulti-user.target.wants.
Step 4 - System-wide prompt and tools
- Copies
configs/profile.d/00-prompt.shto/etc/profile.d/and marks it executable. This sets a colored PS1 for login shells. Non-login interactive shells get their PS1 from~/.bashrc(set bycustomize-bashrc.sh). - Installs tools system-wide:
get-arkade.sh,get-common-tools.sh,get-btop.sh,get-cfssl.sh,get-websocat.sh, andcurl https://fx.wtf/install.sh | sh.
Step 5 - Root user customizations
- Runs
get-fzf.sh,customize-bashrc.sh,customize-git.sh, andcustomize-vimrc.shforroot.
Step 6 - Create non-root user
- Runs
add-user.shto create$USER(default:ibtisam, UID 1001) withsudogroup membership and passwordlessNOPASSWD:ALLsudoers entry. - Switches to
USER $USER, setsHOME=/home/$USER.
Step 7 - User-specific tools and welcome (order matters)
- Runs
get-code-server.sh,get-fzf.sh,customize-bashrc.sh,customize-git.sh(withUSER=$USER), andcustomize-vimrc.shfor the non-root user. - Last instruction:
COPY welcome $HOME/.welcome- placed after all scriptRUNsteps to ensure the file is not consumed during build.
No
CMDorENTRYPOINTis set. The iximiuz platform boots the VM with its own kernel and does not execute the image as a Docker container. Setting an entrypoint here would be incorrect.
2. Build and Push via GitHub Actions¶
The canonical build is defined in .github/workflows/build-ubuntu-rootfs.yml.
Triggers:
pushtomainwhen anything underiximiuz/rootfs/ubuntu/**(exceptREADME.md) changes, or when the workflow file changes.- Pull requests touching the same paths.
- Manual
workflow_dispatch.
Key build behavior:
- Authenticates to GHCR via
secrets.GITHUB_TOKEN. - Generates tags via
docker/metadata-action:latest(default branch),sha-<short-sha>,YYYY-MM-DD. - Runs
docker/build-push-actionwithcontext: ./iximiuz/rootfs/ubuntu,platforms: linux/amd64,push: true(non-PR events), and thebuild-argsabove. - Prints the final image digest on completion.
Verification¶
Correct: Inspect the Registry Image¶
After a CI push or manual docker push, verify image metadata:
skopeo inspect docker://ghcr.io/ibtisam-iq/ubuntu-24-04-rootfs:latest \
| jq '{
name: .Name,
base: .Labels["org.opencontainers.image.base.name"],
created: .Labels["org.opencontainers.image.created"],
documentation: .Labels["org.opencontainers.image.documentation"],
authors: .Labels["org.opencontainers.image.authors"]
}'
Correct: Boot in an iximiuz Manifest¶
The only valid way to verify runtime behavior (systemd, SSH, prompt, welcome) is to boot the image inside an iximiuz microVM. Use or adapt an existing manifest:
# iximiuz/manifests/ubuntu-base-test.yml
machines:
- name: ubuntu-base
drives:
- source: oci://ghcr.io/ibtisam-iq/ubuntu-24-04-rootfs:latest
mount: /
size: 50GiB
Once the VM is running, connect and verify:
# Inside the VM via labctl ssh or the Labs terminal:
systemctl is-system-running # Expected: running
systemctl status ssh # Expected: active (running)
echo $PS1 # Expected: colored \u@\h:\w $ prompt
cat ~/.welcome # Expected: banner on first login; file is deleted after
Not Valid: Plain docker run¶
Running this image with docker run (even with --privileged --cgroupns=host) does not produce a working systemd environment because:
- The image has no
CMD/ENTRYPOINT- Docker falls back to the base Ubuntu shell (/bin/bash), not systemd. - SSH host keys are intentionally absent -
sshdwill not start without them. - The image is not designed or tested for Docker container execution.
Errors like System has not been booted with systemd as init system (PID 1) or cat: /home/ibtisam/.welcome: No such file or directory when using docker exec are expected in this context - they confirm the image is correctly built for microVM use only.
Integration and Usage¶
As a Base for Child Images¶
Examples:
- Jenkins rootfs:
iximiuz/rootfs/jenkins/Dockerfile- adds Java 21, Jenkins LTS, Nginx, and cloudflared. - Dev Machine:
iximiuz/rootfs/dev/machine/Dockerfile- layers workstation tooling.
Guidelines for child Dockerfiles:
- Start with
USER rootto install services and system packages. - Enable services with
systemctl enable <service>during build. - Place
COPY welcome $HOME/.welcomeafter allRUN customize-bashrc.shsteps. - End with
USER rootso the platform boots the VM with correct permissions. - Set
CMD ["/lib/systemd/systemd"]only if the child image also needs to run as a standalone Docker container (e.g., fordocker run-based testing). The ubuntu base itself must not have this.
Optional: Direct Use as a Playground Rootfs¶
The base image can be booted directly by pointing a manifest's drive at it (as shown in Verification above). Typically use Dev Machine or another child image instead.