Skip to content

feat(servers): panel host stack status and safe updates - #51

Merged
agentHits merged 16 commits into
AgentHits-Devfrom
feat/panel-host-stack
Oct 9, 2026
Merged

agentHits merged 16 commits into
AgentHits-Devfrom
feat/panel-host-stack

Conversation

@agentHits

@agentHits agentHits commented Oct 8, 2026 •

Copy link
Copy Markdown
Owner

Summary

Adds a Panel host card to the Remote Servers page. It shows the services that run on the panel's own host, compares each with its pinned target, and lets the operator update the safe ones from the panel.

Component Installed Target Updated from the panel
Panel (dokploy) the running image latest build through the existing Web Server Update dialog
Traefik the running tag pinned Traefik yes, with a checkbox
Redis the running tag pinned Redis yes, with a checkbox
Docker the daemon version pinned Docker no: a maintenance-window command is shown, because the daemon restart also stops the panel
Postgres the running tag pinned Postgres no: a major version change needs a migration; the current major version is kept

Replacement details:

  • Traefik keeps the networks, restart policy and read-only Docker socket of the running container, and publishes port 80 only if the running container does.
  • Redis uses a pinned image pull, docker service update --update-order stop-first --update-failure-action rollback, and a wait for PONG with a timeout.
  • A panel update is refused while a host component update is running.

Live check on the panel host

Run with the same generated scripts, from this branch, before merging:

  • Redis 7 to 8.10.2: the service ended at 1/1, PONG, and an external probe of the admin site made 46 requests with no failure.
  • Traefik 3.6.1 to 3.7.14: the admin site made 568 requests with one failure, a gap of about a second. Networks, restart policy, read-only socket and published ports (443 only) are the same as before.

Fixes found by the live check: the generated container published port 80 although the running one did not, dropped the read-only flag on the socket, and attached the default bridge network.

Test plan

  • biome check passes on each changed file; tsc --noEmit passes in packages/server and apps/dokploy.
  • Full apps/dokploy suite: 2577 pass. The 4 failures are in application.real.test.ts, which needs a local swarm manager.

🤖 Generated with Claude Code

agentHits and others added 16 commits October 8, 2026 18:13
buildComponentUpdateSteps returns the pre-download gate and the steps of the
selected components. wrapComponentUpdateSteps adds the shell header and the
done marker. buildComponentUpdateScript composes the two with the same output,
so the SSH update path is unchanged, and the panel host update can run the
Traefik steps as they are.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
buildHostStackRows turns the versions read on the panel host into one row per
component: panel, docker, traefik, postgres and redis. Each row reports its
installed and target version, whether it is outdated, and how it can be
updated: from the Web Server Update dialog (panel), from the panel itself
(traefik, redis), or only in the maintenance window (docker, postgres).
Postgres is compared by major version, and a major change is reported as
needing a migration. Values that cannot be read are unknown, never outdated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
updateHostStackComponents runs one bash script on the panel host. It replaces
Traefik with the steps the SSH update path already uses, and updates the Redis
service to the pinned image with stop-first and rollback. Redis is pulled
before anything changes. The Redis update waits until the swarm reports the
update as completed and redis-cli answers PONG, within a timeout; a rollback,
a pause, a refused update or a timeout fails the run. A second update is
refused while one is running.

readHostStackRows reads the installed versions with read-only docker commands
and the panel's own update check, which has a timeout.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s page

settings.hostStack returns the rows of the panel host components. It is
limited to local host access (owner or admin) and returns nothing on cloud.
settings.updateHostStack is a subscription that runs the Traefik and Redis
updates on the panel host and streams their logs. It refuses docker, postgres
and panel with the next step, refuses while a panel update or another host
update is running, and writes an audit entry. Both procedures are classified
in the procedure access map; the update is dangerous, like the other host
settings changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A "Panel host (this server)" card sits above the server cards. Its Components
button opens the panel host rows with the installed and target versions, the
status and the action for each component. Traefik and Redis can be updated
from the dialog after a confirmation, with a warning that Traefik is recreated
and the admin site is unreachable for a few seconds. Docker and Postgres show
the maintenance-window command, and the panel row opens the Web Server Update
dialog. The card is hidden for users who cannot manage the panel host.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ostgres

Docker and Postgres are always updated in the maintenance window, so their
rows keep the reason and the exact installer command even when they are
current. The card can then show the command at any time. Traefik and Redis
rows carry neither, since the panel runs them. A Postgres row whose version
cannot be read gets no command, because the image it would keep is unknown.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The rows the panel can update always show a checkbox. It is enabled only when
the component is outdated, so an up-to-date component cannot be selected by
accident. Manual rows show their reason and command, and unknown rows show
the reason they could not be read.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The host components update replaces Traefik and restarts Redis from a script
run by this panel. A panel update restarts the panel container, which would
stop that script in the middle of the change. The update mutation now refuses
while the host components update runs, the same way the host update refuses
while a panel update runs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ainer

The installer mounts the socket read-only, but the generated container
for remote servers and the panel host dropped the :ro flag. Keep the same
mount in both places, so an update does not change how Traefik sees the
daemon.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The panel host's Traefik publishes 443 only, but the generated container
and the installer always published 80 as well. A replacement would have
opened port 80 without anyone asking for it. Read the current publication
before the old container stops and reuse it for the new one and for the
restore; a fresh install still publishes 80.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The settings router reads the host stack constant and update guard, so the
module mocks in the settings boundary tests must provide them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ontainer did not have it

A replacement container is created with the default bridge network. The
live Traefik had no bridge attachment, so the update added one it did not
have before. Detach bridge after the new container runs, but only when the
container being replaced was not on it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An empty array expanded under set -u fails on bash 3.2, which the macOS
CI runner uses. Use the guarded form, so port 80 is passed only when it is
published.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d of refusing

The operator update used to stop before any change when the installed
Postgres major version differed from the pinned one. It now prints
"Postgres <N> stays. A major upgrade is a separate migration; this update
does not change it." and continues:

- no Postgres backup, no Postgres pre-pull and no Postgres swap in that run
- Redis, Traefik, the panel and the Docker Engine step run as before
- when the majors match, the backup and the swap behave as before

postgres_ready now checks the image the service actually runs. Checking the
pinned image would never find a kept older Postgres, so the Docker Engine
health check would roll the upgrade back.

Tests: the refusal test now expects the skip line, no backup, no swap and the
other steps in order. A new Docker Engine upgrade test covers a kept Postgres.
The fake docker ps honors the ancestor filter for dokploy-postgres, so the
readiness check against the pinned image fails that test.

Docs: docs/server-maintenance.md is a new operator guide. The install guide
describes the new Postgres behaviour.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The operator guide now says only port 443 (TCP and UDP) serves domain
traffic, port 80 is never opened, and certificates renew only over 443
through the TLS challenge. The UFW note no longer lists port 80. The note
about the panel's own port 3000 is unchanged. The installer code is not
changed here; that change is on a separate branch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The Traefik step does nothing when the running container already uses the
  target image, so a repeated run (for example after the panel reconnects)
  causes no second outage.
- The update lock is taken synchronously before the audit call and released on
  every exit path, and a panel update cannot start while a host update runs.
- The Postgres row compares the full tag, so 18 against 18.6 shows as needing
  an update. The manual command is shown only for a different major.
- The dialog looks for the done and failed markers in the accumulated log, so a
  marker split across two chunks is not missed.

Tests cover the repeated run, the lock race and release, the row comparison,
and the split marker.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@agentHits agentHits closed this Oct 9, 2026
@agentHits agentHits reopened this Oct 9, 2026
@agentHits
agentHits merged commit 37dbb15 into AgentHits-Dev Oct 9, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant