makerfloss/src/containers/podman
Lars Rossen be50a7d900 fix(podman): skip OIDC unit restart via configureService "none"
Portainer registers the OIDC provider through its own API and ships no podman-configure-oidc.service, so identity:identity only logged a spurious restart failure each update. Relies on the configureService="none" support just added upstream.
2026-09-01 20:23:48 +02:00
..
DESIGN.md Merge portainer into podman: engine plus GUI in one module 2026-08-25 09:30:11 +02:00
INSTALL.md podman: document internet exposure and what identity does not gate 2026-08-25 09:56:01 +02:00
install.sh Merge portainer into podman: engine plus GUI in one module 2026-08-25 09:30:11 +02:00
podman.json fix(podman): skip OIDC unit restart via configureService "none" 2026-09-01 20:23:48 +02:00
README.md podman: document internet exposure and what identity does not gate 2026-08-25 09:56:01 +02:00
test.sh Merge portainer into podman: engine plus GUI in one module 2026-08-25 09:30:11 +02:00
update.sh Merge portainer into podman: engine plus GUI in one module 2026-08-25 09:30:11 +02:00

Podman — container host with a Portainer console

Primary audience: TAPPaaS operator who wants a place to run OCI containers, and several people managing them.

A Debian 13 (trixie) VM running Podman as the container engine, with Portainer CE as its web interface. Portainer runs as a container on the very engine it manages: Podman is the foundation, Portainer is the GUI.

People sign in with their TAPPaaS identity over OIDC — no per-VM Linux accounts to hand out. From the console they create, start, stop, exec into and inspect containers, pull images and deploy compose stacks — on this VM, and on any other host in the zone running a Portainer agent.

Two engines, on purpose

Socket Who uses it Why
rootful /run/podman/podman.sock Portainer It drives the Docker-compatible API; Portainer does not support rootless
rootless user socket (tappaas) people who ssh in The safer way to run day-to-day containers by hand

Portainer's access to the rootful socket is root-equivalent on this VM. That is inherent to every container manager with socket access — treat the VM as a lab host and put nothing precious on it.

Why Portainer and not Cockpit

The first version of this module shipped Cockpit with cockpit-podman. It cannot meet either requirement: Cockpit is not an OIDC client and cannot be configured into one, it always needs a local Unix account (cockpit-bridge runs in a PAM session as the user), and its multi-host switcher is deprecated and disabled by default — the project states it "cannot be secure". The full analysis, including the two options that were rejected, is in DESIGN.md.

komodo is the GPL alternative that was weighed against Portainer.

Gateway role

Given an explicit proxyDomain, this module publishes at a name of your choosing rather than the usual <module>.<environment-domain>. Pointing it at the environment's own domain makes it that environment's front door:

module-manager module add podman --environment lab1 --proxyDomain lab1.makerfloss.eu

…and the console answers at https://lab1.makerfloss.eu with no extra DNS label.

Identity, and what CE cannot do

identity:identity creates the OIDC application in Authentik, binds it to the allowed groups — that binding is the access gate, since Authentik fails open without one — and writes OIDC_CLIENT_ID, OIDC_CLIENT_SECRET and OIDC_DISCOVERY_URI to /etc/secrets/podman.env. update.sh reads the discovery document and PUTs the endpoints into Portainer's /api/settings.

Mapping Authentik groups onto Portainer teams is a Business Edition feature. In CE everyone who passes the Authentik binding lands with the same access. For a shared lab that is the intent; if you need per-person isolation, CE will not give it to you.

Dependencies

Depends on Purpose
cluster:vm Creates the Debian 13 VM from the cloud image
templates:debian OS prep (apt update/upgrade + qemu-guest-agent)
backup:vm Scheduled VM backup to PBS
network:proxy The published HTTPS name, upstream on :9443
identity:identity OIDC application, group binding, client credentials

Placement

No zone0 is set, so the VM lands in the environment's zone (ADR-007 P5). proxyAllowedZones unset gives the internal default — every Active service zone plus home, work, mgmt and the netbird overlay, but not the internet. Note the operator's admin-VPN overlay (admin, 10.255.1.0/24) is not in that default, so a remote admin on the WireGuard tunnel gets 403 too. Set proxyAllowedZones: ["internet"] to publish it — but read the warning below first.

Publishing to the internet

proxyAllowedZones: ["internet"] removes the network restriction, leaving Authentik as the gate. That is almost true, and the gap matters:

Enabling OAuth does not disable Portainer's internal login. POST /api/auth stays live and still accepts the break-glass admin, whatever the UI shows — verified against the public endpoint, which answers 422 Invalid credentials rather than refusing the path. So publishing the console also publishes a username/password path for the local admin.

The password is 32 random base64 characters, so guessing it is not the threat; an authentication bypass in Portainer itself would be. If you want "gated by identity" to be literally true, promote an OIDC user to administrator and then delete the local admin — accepting that an Authentik outage then locks everyone out until the volume is restored.

Sizing

2 vCPU / 2 GB RAM / 32 GB disk. Podman and Portainer are small; the disk is for the images and volumes of whatever gets run here.

For installation steps see INSTALL.md.