Deployments, auto deploy, service routes, live logs, metrics and a terminal #1

Merged
reudy merged 1 commit from agent/deployments into main 2026-09-27 21:28:26 +02:00 AGit
Member

Adds four Dokploy-style features to the panel.

Deployments

  • Deploy, restart, git sync and "save & deploy" run in the background, one at a time per app. A newer request replaces one that is still queued.
  • Every run is kept (the last 50 per app, in SQLite, with their logs). The new Deployments tab streams the log live.
  • A running or queued deployment can be cancelled.
  • Any earlier deployment can be redeployed. For git apps that deploys its commit; for compose apps, its saved compose file. That makes rollback one click.
  • The card header shows the last deploy, and deployments started elsewhere (webhooks) raise a toast.

Auto deploy

  • The Git tab has a switch that deploys on every push to the app's branch.
  • With a Forgejo token stored, the panel adds the webhook to the repository itself. For GitHub or CI it shows the URL and secret.
  • POST /hooks/<app> accepts Forgejo, Gitea and GitHub HMAC signatures, or the secret as a token. Pushes to other branches are ignored.

Domains without port juggling

  • A domain points at a container port: 8080, or web:8080 when the compose file has several services.
  • The panel picks a free 127.0.0.1 port in 18000–19999, and panelctl publishes it through a generated .panel-ports.yaml override. Compose files no longer need ports:.
  • The New app dialog suggests targets from a pasted compose file.
  • Existing host:port routes keep working unchanged.

Logs, monitoring and terminal

  • Logs stream live, with a service picker, a filter, and copy/download.
  • The Monitoring tab shows an hour of CPU and memory graphs per container, from podman stats.
  • The Terminal tab opens a shell in a container (podman exec over a WebSocket, using xterm.js). The Nix package bundles xterm.js, pinned by hash.

Security: the webhook route skips Authelia. The NixOS module sends /hooks/* straight to the panel. Caddy matches the cleaned path but forwards the original, so POST /apps/x/remove/../../../hooks/x would reach "remove app x" without a login. I reproduced that with a real Caddy. Two fixes:

  • Caddy tags requests on that route with X-Panel-Hook, and the panel accepts only POST /hooks/<name> with it.
  • The panel refuses . and .. path segments, including encoded ones.

With a real Caddy in front, the traversal variants now get 400 and 403, and the app survives. The option services.reudy-panel.webhooks = false turns the route off.

Tests. I didn't have a real Podman here, so these ran against fake podman / podman-compose scripts:

  • 64 API checks:
    • queueing, superseding and cancelling deploys;
    • failure errors and both kinds of rollback;
    • service targets and port reuse;
    • signed, unsigned and wrong-branch webhooks;
    • Forgejo hook registration and cleanup on removal;
    • the SSE streams, stats, and the WebSocket terminal (including its origin and container checks);
    • the webhook-route guards.
  • Playwright walk-through: every new tab, a webhook deploy noticed by the UI, and a phone-width view.
  • Nix: nix flake check passes, and the package builds with the vendored xterm.js and PyYAML. The built package served the UI and deployed an app, and the vps config evaluates with this branch.

Not tested on the server: real podman stats output (it's parsed defensively), and podman exec -it through a real container.

Deploying: after merging, on the VPS: cd ~/nixos && nix flake update panel && nixos-rebuild switch --flake .#vps --sudo. For Forgejo to register webhooks, the stored token needs write access to repositories.

🤖 Generated with Claude Code

https://claude.ai/code/session_01UbWSNkXxZhYf7eqHTyx3Bf

Adds four Dokploy-style features to the panel. **Deployments** - Deploy, restart, git sync and "save & deploy" run in the background, one at a time per app. A newer request replaces one that is still queued. - Every run is kept (the last 50 per app, in SQLite, with their logs). The new Deployments tab streams the log live. - A running or queued deployment can be cancelled. - Any earlier deployment can be redeployed. For git apps that deploys its commit; for compose apps, its saved compose file. That makes rollback one click. - The card header shows the last deploy, and deployments started elsewhere (webhooks) raise a toast. **Auto deploy** - The Git tab has a switch that deploys on every push to the app's branch. - With a Forgejo token stored, the panel adds the webhook to the repository itself. For GitHub or CI it shows the URL and secret. - `POST /hooks/<app>` accepts Forgejo, Gitea and GitHub HMAC signatures, or the secret as a token. Pushes to other branches are ignored. **Domains without port juggling** - A domain points at a container port: `8080`, or `web:8080` when the compose file has several services. - The panel picks a free `127.0.0.1` port in 18000–19999, and `panelctl` publishes it through a generated `.panel-ports.yaml` override. Compose files no longer need `ports:`. - The New app dialog suggests targets from a pasted compose file. - Existing `host:port` routes keep working unchanged. **Logs, monitoring and terminal** - Logs stream live, with a service picker, a filter, and copy/download. - The Monitoring tab shows an hour of CPU and memory graphs per container, from `podman stats`. - The Terminal tab opens a shell in a container (`podman exec` over a WebSocket, using xterm.js). The Nix package bundles xterm.js, pinned by hash. **Security: the webhook route skips Authelia.** The NixOS module sends `/hooks/*` straight to the panel. Caddy matches the *cleaned* path but forwards the original, so `POST /apps/x/remove/../../../hooks/x` would reach "remove app x" without a login. I reproduced that with a real Caddy. Two fixes: - Caddy tags requests on that route with `X-Panel-Hook`, and the panel accepts only `POST /hooks/<name>` with it. - The panel refuses `.` and `..` path segments, including encoded ones. With a real Caddy in front, the traversal variants now get 400 and 403, and the app survives. The option `services.reudy-panel.webhooks = false` turns the route off. **Tests.** I didn't have a real Podman here, so these ran against fake `podman` / `podman-compose` scripts: - **64 API checks:** - queueing, superseding and cancelling deploys; - failure errors and both kinds of rollback; - service targets and port reuse; - signed, unsigned and wrong-branch webhooks; - Forgejo hook registration and cleanup on removal; - the SSE streams, stats, and the WebSocket terminal (including its origin and container checks); - the webhook-route guards. - **Playwright walk-through:** every new tab, a webhook deploy noticed by the UI, and a phone-width view. - **Nix:** `nix flake check` passes, and the package builds with the vendored xterm.js and PyYAML. The built package served the UI and deployed an app, and the `vps` config evaluates with this branch. **Not tested on the server:** real `podman stats` output (it's parsed defensively), and `podman exec -it` through a real container. **Deploying:** after merging, on the VPS: `cd ~/nixos && nix flake update panel && nixos-rebuild switch --flake .#vps --sudo`. For Forgejo to register webhooks, the stored token needs write access to repositories. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01UbWSNkXxZhYf7eqHTyx3Bf
agent added 1 commit 2026-09-27 21:05:20 +02:00
Deployments: deploy, restart and git sync now run in the background, one at
a time per app (a newer request replaces a queued one). Each run is recorded
in SQLite with its log, streamed to the UI while it runs, and can be
cancelled. Any earlier deployment can be deployed again, which rolls back to
its commit, or to its saved compose file for compose apps.

Auto deploy: POST /hooks/<app>, verified with the app's secret (Forgejo,
Gitea and GitHub HMAC signatures, or the secret as a token for CI). With a
Forgejo token stored, the panel adds the webhook to the repository itself.
The NixOS module routes /hooks/* past Authelia. Caddy matches the cleaned
path but forwards the original, so the panel refuses dot segments and only
accepts webhook deliveries from that route (tagged with X-Panel-Hook).

Domains: a route can point at a compose service's container port
("web:8080"). The panel picks a free 127.0.0.1 port and panelctl publishes
it through a generated .panel-ports.yaml override, so compose files need no
ports: section. Existing host:port upstreams keep working.

Logs stream live over server-sent events, with service and text filters.
A sampler keeps an hour of CPU and memory per container for the new
Monitoring tab. The Terminal tab opens `podman exec` in a container over a
WebSocket, using xterm.js bundled by the Nix package.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UbWSNkXxZhYf7eqHTyx3Bf
reudy merged commit d9d0136d70 into main 2026-09-27 21:28:26 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: reudy-net/panel#1
No description provided.