Skip to content

Latest commit

 

History

History
124 lines (95 loc) · 4.29 KB

File metadata and controls

124 lines (95 loc) · 4.29 KB

workloadctl

Declarative rootless container workload manager for Linux. Define workloads as TOML files, and workloadctl handles user creation, systemd service generation, volume management, secrets, and container lifecycle — all running as isolated unprivileged users with rootless podman.

What's different about Quadlets?

Podman Quadlets generate systemd units from container specs — workloadctl goes further by managing the full workload lifecycle:

  • Declarative TOML configs instead of systemd unit syntax and raw podman flags
  • Automatic user isolation — each workload gets a dedicated system user (_wl-<name>) with its own UID, subuid/subgid ranges, and rootless podman instance. Quadlets typically run all containers under a single user.
  • Lifecycle management — user creation, home/volume directory setup, image transfer, update with automatic rollback, backup/restore, orphan cleanup
  • Hardware shortcuts--gpu amd, --audio, --input, --virtualization expand to the right devices, groups, and mounts
  • TPM2-encrypted secrets via systemd credentials, with portable export/import
  • bootc-ready — drop TOMLs into an image, encrypted secrets safe to commit, everything self-provisions on first boot

Quadlets solve "generate a systemd unit from a container spec." workloadctl solves "declare a workload and have everything from OS user to running container handled automatically."

Requirements

  • Fedora 41+ (or any systemd + podman 5.3+ Linux)
  • Python 3.11+
  • No bootc or immutable OS required (works on standard Fedora too)

Install

From RPM (local build):

cd workloadctl
rpmbuild -bb --define "_topdir $(pwd)/rpmbuild" \
  --define "_sourcedir $(pwd)" rpm/workloadctl.spec
sudo dnf install rpmbuild/RPMS/noarch/workloadctl-*.rpm

Quick start

# Create and start a workload in one command
sudo workloadctl create webserver \
  --image docker.io/nginxinc/nginx-unprivileged:alpine \
  --ports 8080:8080 \
  --enable

# Check status
workloadctl status webserver

# View logs
workloadctl logs webserver

Or write a TOML config directly:

sudo tee /etc/workloads.d/webserver.toml <<'EOF'
[workload]
name = "webserver"
image = "docker.io/nginxinc/nginx-unprivileged:alpine"
enabled = true

[network]
mode = "pasta"
ports = ["8080:8080"]
EOF

sudo workloadctl enable webserver

How it works

Each workload gets:

  • A dedicated locked-down system user (_wl-<name>)
  • Its own UID/subuid namespace for rootless podman
  • A systemd service generated at boot from its TOML config
  • Automatic volume directory creation under /var/lib/workloads/<name>/
  • Optional TPM2-encrypted secrets via systemd credentials

At boot, a tiny shell systemd generator emits an early-boot oneshot service (workload-generate.service) that runs the Python workload-generate script. The script reads /etc/workloads.d/*.toml and writes per-workload unit files into /run/systemd/system/ before basic.target is reached. The Python work is kept out of generator context because systemd expects generators to be fast and minimal (see systemd.generator(7)) — see docs/workloads.md for the full architecture.

Key commands

Command Description
workloadctl create Generate a workload TOML config
workloadctl enable Create user, transfer image, start service
workloadctl disable Stop service, optionally purge user/data
workloadctl status Show workload status and resource usage
workloadctl logs View workload container logs
workloadctl reboot Soft-reboot a systemd container (keeps overlay)
workloadctl recreate Recreate container from image (destroys overlay)
workloadctl update Pull new image and recreate
workloadctl list List all configured workloads
workloadctl secret Manage TPM2-encrypted credentials

Most mutating commands require sudo. Read-only commands work as any user.

Documentation

License

MIT