From 1d07a9421d2e491b89bf3986a2041090a4da5bb6 Mon Sep 17 00:00:00 2001 From: Christopher Bartz Date: Thu, 8 Oct 2026 14:20:55 +0000 Subject: [PATCH 1/3] fix(image): set XDG_RUNTIME_DIR for the GARM agent GARM's agent mode starts the runner from garm-agent.service, a system service without a login session, so jobs inherit no XDG_RUNTIME_DIR. Tools that keep per-user state there (skopeo, podman, buildah) then fall back to the root-owned /run/containers and fail. Add a systemd drop-in pointing it at /run/user/, which lingering already keeps alive. --- app/pyproject.toml | 2 +- .../templates/cloud-init.sh.j2 | 8 ++++++++ app/tests/integration/commands.py | 5 +++++ app/tests/unit/test_openstack_builder.py | 8 ++++++++ 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/app/pyproject.toml b/app/pyproject.toml index 227ed7fa..85eae8cc 100644 --- a/app/pyproject.toml +++ b/app/pyproject.toml @@ -3,7 +3,7 @@ [project] name = "github-runner-image-builder" -version = "0.14.2" +version = "0.14.3" authors = [ { name = "Canonical IS DevOps", email = "is-devops-team@canonical.com" }, ] diff --git a/app/src/github_runner_image_builder/templates/cloud-init.sh.j2 b/app/src/github_runner_image_builder/templates/cloud-init.sh.j2 index b1e4cb8b..9fc41242 100644 --- a/app/src/github_runner_image_builder/templates/cloud-init.sh.j2 +++ b/app/src/github_runner_image_builder/templates/cloud-init.sh.j2 @@ -195,6 +195,14 @@ function configure_system_users() { # removes /run/user/ once the last session ends. A runner started as a system service # has no login session, so linger is what keeps both alive. /usr/bin/loginctl enable-linger ubuntu + # For the same reason the GARM agent's jobs inherit no XDG_RUNTIME_DIR, so tools keeping + # per-user state there (skopeo, podman, buildah) fall back to the root-owned /run/containers. + # systemd applies the drop-in once GARM installs garm-agent.service on the runner. + /usr/bin/mkdir -p /etc/systemd/system/garm-agent.service.d + /usr/bin/cat > /etc/systemd/system/garm-agent.service.d/xdg-runtime-dir.conf < once the last session ends. A runner started as a system service # has no login session, so linger is what keeps both alive. /usr/bin/loginctl enable-linger ubuntu + # For the same reason the GARM agent's jobs inherit no XDG_RUNTIME_DIR, so tools keeping + # per-user state there (skopeo, podman, buildah) fall back to the root-owned /run/containers. + # systemd applies the drop-in once GARM installs garm-agent.service on the runner. + /usr/bin/mkdir -p /etc/systemd/system/garm-agent.service.d + /usr/bin/cat > /etc/systemd/system/garm-agent.service.d/xdg-runtime-dir.conf < Date: Thu, 8 Oct 2026 14:21:19 +0000 Subject: [PATCH 2/3] docs: add changelog entry for the XDG_RUNTIME_DIR fix --- docs/changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 7ca0fa0d..a4e588df 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ +## [#245 Set `XDG_RUNTIME_DIR` for the GARM agent](https://github.com/canonical/github-runner-image-builder-operator/pull/245) (2026-10-08) + +- Set `XDG_RUNTIME_DIR` for the runner started by the GARM agent. The agent runs as a system service without a login session, so jobs had no `XDG_RUNTIME_DIR` and tools that keep per-user state there, such as `skopeo login`, `podman` and `buildah`, failed with `mkdir /run/containers: permission denied`. + ## [#244](https://github.com/canonical/github-runner-image-builder-operator/pull/244) (2026-09-23) - Ensure the `runner` group alongside the `runner` user. From bad4c02ee124d907875a677020d647cbe48bdaf1 Mon Sep 17 00:00:00 2001 From: Christopher Bartz Date: Thu, 8 Oct 2026 14:39:56 +0000 Subject: [PATCH 3/3] fix(image): run the GARM agent in a PAM login session Setting XDG_RUNTIME_DIR alone restores one piece of what the previous runner manager's su - provided. A PAMName=su-l drop-in restores the session itself (XDG_RUNTIME_DIR, the user D-Bus bus, pam_env, pam_limits), without the TTY-oriented modules of the login stack. --- .../templates/cloud-init.sh.j2 | 11 ++++++----- app/tests/integration/commands.py | 5 ++--- app/tests/unit/test_openstack_builder.py | 11 ++++++----- docs/changelog.md | 4 ++-- 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/app/src/github_runner_image_builder/templates/cloud-init.sh.j2 b/app/src/github_runner_image_builder/templates/cloud-init.sh.j2 index 9fc41242..e9aebdda 100644 --- a/app/src/github_runner_image_builder/templates/cloud-init.sh.j2 +++ b/app/src/github_runner_image_builder/templates/cloud-init.sh.j2 @@ -195,13 +195,14 @@ function configure_system_users() { # removes /run/user/ once the last session ends. A runner started as a system service # has no login session, so linger is what keeps both alive. /usr/bin/loginctl enable-linger ubuntu - # For the same reason the GARM agent's jobs inherit no XDG_RUNTIME_DIR, so tools keeping - # per-user state there (skopeo, podman, buildah) fall back to the root-owned /run/containers. - # systemd applies the drop-in once GARM installs garm-agent.service on the runner. + # The GARM agent runs the runner from that system service, so jobs lack what a login session + # provides (XDG_RUNTIME_DIR, the user D-Bus bus) and tools like skopeo and podman fall back to + # the root-owned /run/containers. A PAM session restores it, as `su -` did for the previous + # runner manager; systemd applies the drop-in once GARM installs garm-agent.service. /usr/bin/mkdir -p /etc/systemd/system/garm-agent.service.d - /usr/bin/cat > /etc/systemd/system/garm-agent.service.d/xdg-runtime-dir.conf < /etc/systemd/system/garm-agent.service.d/login-session.conf < once the last session ends. A runner started as a system service # has no login session, so linger is what keeps both alive. /usr/bin/loginctl enable-linger ubuntu - # For the same reason the GARM agent's jobs inherit no XDG_RUNTIME_DIR, so tools keeping - # per-user state there (skopeo, podman, buildah) fall back to the root-owned /run/containers. - # systemd applies the drop-in once GARM installs garm-agent.service on the runner. + # The GARM agent runs the runner from that system service, so jobs lack what a login session + # provides (XDG_RUNTIME_DIR, the user D-Bus bus) and tools like skopeo and podman fall back to + # the root-owned /run/containers. A PAM session restores it, as `su -` did for the previous + # runner manager; systemd applies the drop-in once GARM installs garm-agent.service. /usr/bin/mkdir -p /etc/systemd/system/garm-agent.service.d - /usr/bin/cat > /etc/systemd/system/garm-agent.service.d/xdg-runtime-dir.conf < /etc/systemd/system/garm-agent.service.d/login-session.conf < -## [#245 Set `XDG_RUNTIME_DIR` for the GARM agent](https://github.com/canonical/github-runner-image-builder-operator/pull/245) (2026-10-08) +## [#245 Run the GARM agent in a login session](https://github.com/canonical/github-runner-image-builder-operator/pull/245) (2026-10-08) -- Set `XDG_RUNTIME_DIR` for the runner started by the GARM agent. The agent runs as a system service without a login session, so jobs had no `XDG_RUNTIME_DIR` and tools that keep per-user state there, such as `skopeo login`, `podman` and `buildah`, failed with `mkdir /run/containers: permission denied`. +- Run the runner started by the GARM agent in a PAM login session, as the previous runner manager did with `su -`. The agent runs as a system service, so jobs had no `XDG_RUNTIME_DIR` or user D-Bus bus, and tools that keep per-user state there, such as `skopeo login`, `podman` and `buildah`, failed with `mkdir /run/containers: permission denied`. ## [#244](https://github.com/canonical/github-runner-image-builder-operator/pull/244) (2026-09-23)