Web UI (panel/frontend/index.html) rewritten: - Cards update in place from a single /status poll instead of being rebuilt on every action, so open tabs, unsaved compose/route edits, logs and the file browser position survive refreshes. Polling speeds up while an operation runs and pauses in background tabs; a header indicator shows when the panel last synced and detects an expired Authelia session. - New-app dialog (starter / compose / git), suggested port and domain, proper confirm dialogs (the old "OK = keep volumes" remove prompt is gone), toasts, an activity drawer with operation output, overflow menu, search, status filters, keyboard shortcuts, deep links, dark mode and mobile layout. - Tabs: overview (containers + routes), compose editor (dirty tracking, Ctrl+S), logs with follow, validated routes editor, file browser with drag-and-drop upload, backups, and a git source tab (deployed commit, check for updates, sync & deploy). API (panel/panel-api.py): - ThreadingHTTPServer so a long deploy no longer blocks every other request. - Per-app operation lock; concurrent writes to a busy app return 409. - GET /status: all apps, routes and container status in one request (statuses gathered in parallel); status reports running/partial/stopped. - Git sync is fetch + hard reset instead of pull-or-reclone, keeps the stored token, reports before/after commits; GET /apps/<name>/repo[?fetch=1]. - Any http(s) git host (e.g. Forgejo), default branch detection, git timeouts, no credential prompts, tokens redacted from errors, and manifest values validated before being written into the bash-sourced manifest. panelctl: - flock around routes.caddy rewrites (util-linux added to the service path). - deploy returns compose output so failures are visible in the UI. - inspect-volumes no longer fails for apps without named podman volumes, which broke the file browser. Docs: README/API.md updated; fixed outdated panelctl init examples. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UbWSNkXxZhYf7eqHTyx3Bf
129 lines
4.3 KiB
Markdown
129 lines
4.3 KiB
Markdown
# panelctl quickstart
|
|
|
|
Minimal container management panel for rootless Podman + Caddy.
|
|
|
|
## Base directory
|
|
|
|
`/var/lib/containers`
|
|
|
|
## Generated structure
|
|
|
|
```
|
|
/var/lib/containers/
|
|
├── stacks/<app>/compose.yaml # Compose file per app
|
|
├── volumes/<app>/data # Persistent volumes
|
|
├── routes/routes.caddy # Single aggregate Caddy routes file
|
|
├── backups/<app>-<timestamp>.zip # Volume backups
|
|
└── state/apps/<app>.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/<app>/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.
|