quadletman¶
quadletman is a browser-based admin UI for running Podman containers on a headless Linux server. Instead of talking to the Podman socket at runtime, it generates and manages Quadlet unit files — the systemd-native way to declare containers as persistent services. Each group of containers lives in a compartment: an isolated environment backed by a dedicated Linux system user, its own volume storage, and its own Podman secret and registry-credential store.
You point a browser at the server, log in with your existing OS credentials, and get a full lifecycle UI: create compartments, define containers and pods, manage volumes and secrets, schedule timers, watch live logs, and monitor resource usage. All without touching the command line.

See docs/features.md for a full feature breakdown.
⚠ Alpha software — not for production use. quadletman is in early development. Interfaces, configuration, and data formats may change without notice between releases. Do not deploy it on systems where stability or data integrity is critical.
Comparison with Similar Tools¶
quadletman targets a specific gap: a headless server-side web UI that manages containers at the systemd unit file level rather than via the Podman socket API.
| Tool | Interface | Creates/edits Quadlet unit files | Per-service OS user isolation | Server-side web UI |
|---|---|---|---|---|
| quadletman | Web (HTMX) | Yes | Yes | Yes |
| cockpit-podman | Web (Cockpit) | No — shows running containers only | No | Yes |
| Podman Desktop | Desktop app (Electron) | Yes (via extension) | No | No |
| Portainer | Web | No | No | Yes |
| Dockge | Web | No — Docker Compose only | No | Yes |
| podman-tui | Terminal (TUI) | No | No | No |
cockpit-podman is the closest server-side alternative. It shows Podman containers (including ones already started by Quadlet units) but does not create or edit unit files, manage system users, or handle volumes with SELinux labels. It is a read/run UI, not a provisioning tool.
Podman Desktop is the only other tool that actually generates and edits Quadlet unit files through a form interface, but it is a developer desktop application requiring an installed GUI environment — not a tool for administering a headless Linux server remotely.
Portainer and Dockge are Docker-centric and treat Podman as a drop-in Docker socket replacement. Neither has any concept of Quadlet unit files, systemd user services, or per-service Linux user isolation.
quadletman's distinctive combination — generating Quadlet unit files, running each service group under its own isolated Linux user, managing host volumes with SELinux contexts, and doing all of this from a browser against a headless server — is not covered by any existing tool.
Requirements¶
- Python 3.12+
- Podman with Quadlet support (Podman 4.4+; build units require Podman 4.5+; bundle import/export requires Podman 5.8+)
- systemd (with
loginctlandmachinectl) - Linux PAM development headers (
pam-devel/libpam0g-dev) - Optional: SELinux tools (
policycoreutils-python-utils) for context management
Installation¶
Fedora / RHEL / AlmaLinux / Rocky Linux (RPM)¶
Ubuntu / Debian (DEB)¶
Generic (any systemd Linux)¶
See docs/packaging.md for build prerequisites, how packages are structured, upgrade instructions, and smoke-test VM setup. See docs/runbook.md for first-time setup, configuration, and day-to-day operations.
With the default configuration, the web UI will be available at http://<host>:8080.
Configuration¶
See docs/runbook.md — Configuration for all
QUADLETMAN_* environment variables and how to set them via /etc/quadletman/quadletman.env.
Architecture¶
See docs/architecture.md for details on compartment roots, helper users, UID/GID mapping, registry logins, Quadlet file generation, bundle export/import, volumes, and systemd user commands.
Development¶
See docs/development.md for setup, running locally, WSL2 notes, contributing guidelines, testing, and database migrations.
Quick start:
uv sync --group dev # install deps
sudo env QUADLETMAN_DB_PATH=/tmp/qm-dev.db \
QUADLETMAN_VOLUMES_BASE=/tmp/qm-volumes \
.venv/bin/quadletman # run as root with dev-isolated data
uv run pytest # run tests (not as root)
Further Reading¶
| Document | Contents |
|---|---|
| docs/runbook.md | Post-install setup, configuration, day-to-day operations, troubleshooting, upgrade, and uninstall |
| docs/features.md | Full feature breakdown — compartments, containers, volumes, scheduling, monitoring, process and connection monitors |
| docs/architecture.md | Compartment roots, helper users, UID/GID mapping, Quadlet files, volumes |
| docs/development.md | Dev setup, running locally, WSL2 (incl. connection monitor limitations), contributing, migrations |
| docs/packaging.md | Build prerequisites, package structure, upgrade instructions, smoke-test VMs |
| docs/testing.md | Unit/integration tests |
| docs/ways-of-working.md | Branch strategy, PR process, CI pipeline, versioning scheme, release process |
| docs/ui-development.md | UI state management, Alpine/HTMX patterns, macros, button styles, modals |
| CLAUDE.md | AI/contributor conventions — code patterns, security checklist, version gating |
Security Notes¶
- The application runs as
rootto manage system users and executesudocommands - It is recommended to put this behind a reverse proxy (nginx/caddy) with HTTPS
- Authentication uses the host's PAM stack — credentials are never stored by quadletman
- Only users in
sudo/wheelgroups are authorized, matching OS admin conventions - Session cookies: HTTPOnly, SameSite=Strict; set
QUADLETMAN_SECURE_COOKIES=truefor the Secure flag (required when serving over HTTPS) - CSRF protection: double-submit cookie pattern — every mutating request must include an
X-CSRF-Tokenheader matching theqm_csrfcookie - Security headers on every response:
X-Frame-Options: DENY,X-Content-Type-Options: nosniff, Content Security Policy,Referrer-Policy: same-origin(HSTS whensecure_cookies=True) - Container image references and bind-mount paths are validated server-side; sensitive host
directories (
/etc,/proc,/sys, etc.) cannot be bind-mounted into containers - File writes use
O_NOFOLLOWto prevent symlink-swap (TOCTOU) attacks inside volume directories QUADLETMAN_TEST_AUTH_USERbypasses PAM entirely — never set this in production; it exists solely for Playwright E2E tests running against a dev server