luminare.infra_edge · v0.2.1
0006. Molecule in systemd containers instead of virtual machines
- Status: accepted
- Date: 2026-10-21
Context
Every role must prove on every pull request that it converges on Debian 13 and Ubuntu 24.04 and
that a second run reports changed=0. The roles manage services, so the test hosts need systemd
as PID 1. CI runs on GitHub-hosted ubuntu-24.04 runners, which have Docker but no nested
virtualization by default. Several projects may run Molecule on one Docker daemon at the same
time.
Considered options
- Molecule with the Docker driver and privileged systemd containers built from a repository-local
Dockerfile on digest-pinned
debian:trixieandubuntu:24.04images. - Molecule with prebuilt community images (for example the
docker-*-ansibleimages). - Molecule with virtual machines (Vagrant or a cloud driver).
Decision
Option 1. The shared pieces live in one place, .config/molecule/: config.yml (driver, both
platforms, provisioner, verifier and the test sequence dependency, destroy, syntax, create, prepare, converge, idempotence, verify, destroy) and Dockerfile.j2 (systemd, Python 3,
python3-apt, sudo, CA certificates; kernel module loading masked). Molecule merges each role’s
roles/<role>/molecule/default/molecule.yml on top of config.yml. Containers run privileged
with the host cgroup namespace, /sys/fs/cgroup mounted read-write and tmpfs on /run and
/run/lock. Instance names are ie-<role>-debian13 and ie-<role>-ubuntu2404 (from
MOLECULE_ROLE), so they never clash with other projects or other roles on a shared daemon.
In CI a plan job lists roles/*/molecule/default/molecule.yml as JSON, and a role × platform
matrix (debian13, ubuntu2404) runs molecule test --platform-name ie-<role>-<platform> once
per pair (45-minute timeout), so a Debian failure and an Ubuntu failure show as separate checks
named molecule-scenario (<role>, <platform>). An aggregate job named molecule reports one
result and passes when no role has a scenario. A unit test keeps the matrix platforms equal to
the platforms of config.yml and fails when a role folder has no scenario. The scenarios run on
pull requests, on demand and weekly, not on pushes to main. Dependabot updates the base image
digests (digests only for ubuntu:24.04, never the tag).
Consequences
- Good: no third-party image; every base layer is pinned and reviewed through Dependabot.
- Good: the same command runs locally and in CI, and adding a role needs only its scenario.
- Bad: containers share the runner’s kernel, so firewall rules loaded into the kernel, sysctl
values and kernel modules cannot be proven; such tasks are tagged
molecule-notestand their checks are listed as limitations until they run on a virtual machine. - Bad: the
moleculecheck is not one of the required checks of themainruleset, so a failing scenario does not block a merge by itself; reviewers have to look at it.
docs/adr/0006-molecule-in-systemd-containers.md on GitHub
Licensed under Apache-2.0. Source: luminare-dev/ansible-collection-infra_edge at v0.2.1.