Renames every domain in the config: Caddy virtual hosts (auth, hello, nextcloud, git, panel), the ACME contact email, Authelia's session cookie domain / portal URL / default redirect and TOTP issuer, Nextcloud's hostName and overwritehost, and Forgejo's DOMAIN (ROOT_URL and ssh clone URLs follow from it; the panel picks them up via panel.nix). Panel docs and examples updated too, and a README paragraph that had run together is split again. Requires DNS for auth/hello/nextcloud/git/panel.reudy.net (or a *.reudy.net wildcard) pointing at the server before deploying. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UbWSNkXxZhYf7eqHTyx3Bf
167 lines
6.3 KiB
Markdown
167 lines
6.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.reudy.net|127.0.0.1:18080" true
|
|
|
|
# Create with multiple routes (different ports, optional path)
|
|
panelctl init myapp "app.reudy.net|127.0.0.1:18081,api.reudy.net|127.0.0.1:18082|/api/*" true
|
|
|
|
# Create with wildcard domain (requires DNS challenge in Caddy)
|
|
panelctl init wild "*.reudy.net|127.0.0.1:18083" false
|
|
|
|
# Change routes later (Caddy reloads automatically)
|
|
panelctl set-routes whoami "whoami.reudy.net|127.0.0.1:18080,who.reudy.net|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.reudy.net` → panel-api with Authelia forward_auth.
|
|
- Open `https://panel.reudy.net` 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 (Forgejo, GitHub or any git host, over https or
|
|
ssh) are cloned to `stacks/<app>/repo`.
|
|
|
|
**Forgejo.** `panel.nix` points the panel at the local Forgejo
|
|
(`PANEL_FORGEJO_URL`, `PANEL_FORGEJO_API_URL`, `PANEL_FORGEJO_SSH_URL`, taken
|
|
from `forgejo.nix`). In the panel's **Settings** you can connect a Forgejo
|
|
access token (read access to repositories and user). With it, the new-app
|
|
dialog lists your repositories and branches, and private ones are cloned over
|
|
https with the token. Without it, public repositories are listed and private
|
|
ones are cloned over ssh with the deploy key. Commit and compare links point at
|
|
Forgejo. The token is stored in `state/panel/forgejo-token` (mode 0600).
|
|
|
|
**SSH / deploy key.** The panel generates an ed25519 key pair in
|
|
`state/panel/ssh/` the first time it is needed. Its public half is shown in
|
|
Settings (and next to ssh URLs); add it as a read-only deploy key to a
|
|
repository — or to your Forgejo account for access to all repositories — to
|
|
clone `ssh://git@git.reudy.net:14922/owner/repo.git` style URLs.
|
|
|
|
**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.
|
|
|
|
### Environment variables
|
|
|
|
Each app can have environment variables (the **Environment** tab, or when
|
|
creating the app; `.env` text can be pasted in). They are stored in
|
|
`state/env/<app>.env` as `KEY=VALUE` lines (mode 0600) — outside the repository
|
|
and stack directory, so git syncs never touch them — and `panelctl` passes them
|
|
to every compose command:
|
|
|
|
- They are always available for `${VAR}` interpolation in the compose file.
|
|
The UI points out variables the compose file uses without a default that
|
|
aren't set.
|
|
- With **Pass to every container** (the default), `deploy`/`restart` also
|
|
generate `stacks/<app>/.panel-env.yaml`, a compose override that lists the
|
|
keys under every service's `environment:`. Compose reads the values from its
|
|
own environment, so they are never quoted into YAML, and they take
|
|
precedence over values set in the compose file.
|
|
|
|
Values must be single-line. Names that would change how podman/compose run
|
|
(`PATH`, `HOME`, `XDG_*`, `DOCKER_*`, `COMPOSE_*`, `PODMAN_*`, …) are rejected.
|
|
Changes apply on the next deploy. Backups do not include variables.
|
|
|
|
### 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.
|