# panelctl quickstart Minimal container management panel for rootless Podman + Caddy. ## Base directory `/var/lib/containers` ## Generated structure ``` /var/lib/containers/ ├── stacks//compose.yaml # Compose file per app ├── volumes//data # Persistent volumes ├── routes/routes.caddy # Single aggregate Caddy routes file ├── backups/-.zip # Volume backups └── state/apps/.env # App manifest ``` All app routes are written to a single `routes/routes.caddy` file that Caddy imports. ## Quick workflow ```bash # Routes are "domain|upstream[|path]" entries, comma-separated. # Create a new app (single route, protected by Authelia) panelctl init whoami "whoami.srazka.com|127.0.0.1:18080" true # Create with multiple routes (different ports, optional path) panelctl init myapp "app.srazka.com|127.0.0.1:18081,api.srazka.com|127.0.0.1:18082|/api/*" true # Create with wildcard domain (requires DNS challenge in Caddy) panelctl init wild "*.srazka.com|127.0.0.1:18083" false # Change routes later (Caddy reloads automatically) panelctl set-routes whoami "whoami.srazka.com|127.0.0.1:18080,who.srazka.com|127.0.0.1:18080" # Deploy (compose up + caddy reload) panelctl deploy whoami # Check container status panelctl status whoami # View logs panelctl logs whoami --tail 50 # Restart containers panelctl restart whoami # Stop containers panelctl stop whoami # Validate compose file panelctl validate-compose whoami # Backup volumes to zip panelctl backup whoami # List backups panelctl list-backups whoami # Restore from backup panelctl restore whoami whoami-20260101-120000.zip # List all apps panelctl list # Show app manifest panelctl show whoami # Remove app (keeps volumes) panelctl remove whoami --keep-volumes # Remove app and all data panelctl remove whoami ``` ## Notes - The default compose file uses `traefik/whoami` for smoke testing — edit before production use. - App names must be lowercase slugs (`[a-z0-9-]`). - Wildcard domains (`*.example.com`) require DNS challenge in Caddy (provider-specific). - Backups stop containers for consistency, then restart if they were running. - If deploy reports `XDG_RUNTIME_DIR` missing, enable lingering: ``` sudo loginctl enable-linger reudy ``` ## Web UI & API - Nix runs `panel-api` as a systemd service on `127.0.0.1:9911`. - Caddy proxies `https://panel.srazka.com` → panel-api with Authelia forward_auth. - Open `https://panel.srazka.com` for the web UI. - API docs: [API.md](API.md) ### Web UI features - Live status: one `/status` poll every few seconds (faster while something is running, paused when the tab is hidden) updates cards in place, so open tabs, unsaved edits and scroll positions are never lost. The header shows when the panel last synced and warns when the Authelia session has expired. - Per-app status (running / partial / stopped), container list, and a busy indicator that is shared between browsers while an operation runs. - New-app dialog: starter container, pasted compose file or git repository; suggests the next free port and a domain based on the app name. - Compose editor with unsaved-changes tracking, Ctrl+S, save & deploy, validate. - Logs with follow mode, routes editor with validation, file browser with drag-and-drop upload, backups with restore (and optional redeploy). - Git source tab: deployed commit, "check for updates", and sync & deploy. - Activity drawer with the output of every operation (e.g. why a deploy failed). - Keyboard: `/` search, `N` new app, `Esc` closes menus. Deep links like `#/whoami/logs` open an app on a specific tab. ### Git-backed apps Apps created from a repository (GitHub, Forgejo/Gitea or any https git host) are cloned to `stacks//repo`. **Sync** fetches the configured branch and hard-resets the checkout to it before redeploying, so the repository is the source of truth: compose edits made in the panel are discarded on the next sync (the UI warns about this). An access token for a private repository is stored in the clone's `.git/config`; use a read-only token. ### Concurrency `panel-api` handles requests concurrently, so a long deploy never blocks status or logs. Mutating operations are serialised per app — a second operation on a busy app gets HTTP 409 — and `panelctl` takes a `flock` on the shared routes file while rewriting it.