feat: rename the binary, user, data directory and image label to dash-proxy - #122
Merged
Conversation
…-proxy Stage 3c part 1 of the server-artifact rename (zoolutions/dash#124). The gem follows in a separate change, behind a MINIMUM_VERSION floor, once this is released — so this must ship first and cannot be reverted after the gem moves. Renamed: cmd/kamal-proxy -> cmd/dash-proxy (which is what makes `make` emit bin/dash-proxy), the container user and its home, the data directory $HOME/.config/dash-proxy, the unix socket, and the org.opencontainers.image.title label the gem prunes by. The routing table needs care, because the gem migrates the old volume by copying it verbatim: a renamed proxy therefore finds a data directory holding kamal-proxy.state and no dash-proxy.state. Config.AdoptLegacyState seeds the current file from the legacy one at boot, before the router reads it. Without that the proxy boots with an empty table and every service has to be re-registered by a deploy — an outage, not a migration. It copies rather than renames, so a rollback to a pre-rename image still finds the table where it looks for it, and StatePath is unconditional so an upgraded host converges onto the new name instead of writing the legacy one forever. Every other file in the data directory (certs/, acme.state, dynamic-domains.state) already has a name that does not mention the proxy, so the copy carries them untouched. DASH_PROXY_SOCKET is accepted alongside KAMAL_PROXY_SOCKET, which the gem still sets on containers booted before the rename. Verified against the published v1.0.0.7 image that the runtime uid is unchanged at 1001 — only its name moves. That is what lets the gem's volume copy use `cp -a` with no chown, and it was an assumption worth checking rather than trusting. The Go module path, the RPC service name, the Prometheus namespace, the /.kamal-proxy/ HTTP routes and the KAMAL_PROXY_* token env names are deliberately unchanged; they have their own follow-up. Refs zoolutions/dash#124
6 tasks
mhenrixon
added a commit
to zoolutions/dash
that referenced
this pull request
Aug 26, 2026
…ss (#130) * feat(proxy): rename container identity to dash and migrate hosts across Stage 3c part 2 of the server-artifact rename (#124), gem side. The proxy image moved first in zoolutions/dash-proxy#122, released as v1.1.0.0. Renamed: the proxy container and its port holder, the docker network every app, accessory and proxy joins, the config volumes, the image title the prune filters key on, and the in-image paths the gem mounts and execs. Three migrations run once per proxy host, before the normal boot, wired through a new Dash::Cli::Proxy::LegacyRename: - The network is bridged rather than renamed, because docker cannot rename one. App containers would be replaced by the next deploy anyway; accessories are not, and without the bridge a renamed proxy cannot reach db or redis. - The config volume is copied before anything starts. It holds the routing table and the ACME account and certificate cache, so losing it means re-issuing every certificate and spending Let's Encrypt rate limits to get back to where we were. - The legacy container is stopped and removed so the renamed one can claim ports 80/443. No port-holder handoff spans two container names, so this stage costs a brief outage per host. Deliberate, and settled in the issue. Each is idempotent and guarded on its destination not existing, so a second deploy is a no-op. The volume copy's guard is negated and leads its chain: shell && and || share precedence and associate left, so the obvious `exists || legacy && create && copy` parses as `((exists || legacy) && create) && copy` and would re-copy the legacy volume over live state on every deploy. Verified against a shell rather than reasoned about. Prune filters now match the pre-rename image title as well as the current one, or `dash proxy remove` would silently leave the old container and image on a host that has not yet migrated. Docker ANDs multiple `--filter label=` values, so that is two chained commands. Nothing removes the legacy network or volume: an operator who wants them gone removes them by hand, and stage 3d deletes this whole path. MINIMUM_VERSION is NOT bumped here - that lands with the docs, once v1.1.0.0 is pullable. Refs #124 * feat(proxy): pin dash-proxy v1.1.0.0 and document the container rename MINIMUM_VERSION moves to v1.1.0.0, the first image with the renamed binary, user, data directory and image title. Verified before pinning: the published tag carries /usr/local/bin/dash-proxy, runs as uid 1001, is multi-arch, and its org.opencontainers.image.title is dash-proxy — which the prune filters key on. bin/sync-proxy-flags invoked `kamal-proxy` to read Cobra's help, so it broke the moment the binary moved. Fixed and the manifest regenerated; the flag surface is unchanged, this release being a rename. The docs claimed three things this stage falsifies: that the container keeps the kamal-proxy name, that dash manages the one kamal booted, and that an upgrade leaves it untouched. Replaced with what actually happens, including the part operators need to plan for — replacing the container costs a short outage on that host, because the old one has to release ports 80/443 before the new one can bind them. Refs #124
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Part 1 of stage 3c, the server-artifact rename (zoolutions/dash#124). This must be released before the gem side merges, and the gem then pins
MINIMUM_VERSIONto the resulting tag — so this is effectively one-way once that happens.makeoutput)cmd/kamal-proxycmd/dash-proxykamal-proxydash-proxy$HOME/.config/kamal-proxy$HOME/.config/dash-proxykamal-proxy.sockdash-proxy.sockorg.opencontainers.image.title=kamal-proxy…=dash-proxyDeliberately not moved, per the issue: the Go module path, the RPC service name, the Prometheus namespace, the
/.kamal-proxy/HTTP routes, and theKAMAL_PROXY_*token env names. Each has its own follow-up.The routing table is the risky part
The gem migrates the old volume by copying it verbatim, so a renamed proxy finds a data directory holding
kamal-proxy.stateand nodash-proxy.state. Left alone it would boot with an empty routing table and every service would need re-registering by a deploy — an outage, not a migration.Config.AdoptLegacyState()runs once at boot, before the router is constructed, and seeds the current file from the legacy one. Two properties matter:StatePathis unconditional, so an upgraded host converges onto the new name. My first attempt hadStatePathreturn whichever file existed; a container test showed that makes the proxy read and write the legacy name forever, which would leave stage 3d unable to retire the fallback.Everything else in the data directory (
certs/,acme.state,dynamic-domains.state,dynamic-redirects.state) already has a name that doesn't mention the proxy, so the copy carries it untouched. That narrows the compatibility surface to one file.Test plan
go test ./...— greengo test ./internal/server/ -run TestConfig_ -count=2— green (the global-registry isolation trap from Global Prometheus registry breaks test isolation: -count=2 panics, cache-metrics assertions flake #104)gofmt -lclean,go vet ./...clean,make buildemitsbin/dash-proxySame numeric uid; only the name moves. So the gem's
cp -aneeds nochown.kamal-proxy.state:It converges onto the new file, keeps the legacy one for rollback, and a second boot logs no adoption — idempotent.
/usr/local/bin/dash-proxy,/home/dash-proxy/.config/dash-proxyowned by the runtime user,dash-proxy --helpworks.Deviations & judgment calls
Deviations
StatePath, for the convergence reason above. The issue's wording ("reading the legacy name as a fallback ... and always writing the new name") describes the intent; a read-fallback alone cannot deliver the second half of it.Judgment calls
CLAUDE.md's "Never Do" rules 1 and 4. They forbid renaming the binary, socket and image label — invariants this change deliberately overturns. Left as-is they would tell the next reader to revert this. Rewritten to keep what's still true (module path and RPC name stay) and name the stage that moved the rest.README.md(86 occurrences). It is full ofkamal-proxy deploy …invocations naming a binary this change removes. Done with a lookbehind-guarded substitution that skips/.kamal-proxy/,KAMAL_PROXY_*andbasecamp/kamal-proxy; the count of those preserved patterns is identical before and after.--data-dirhelp strings, not just the one the issue cites — changing the default made it wrong inrun.goandexport.gotoo.Discovery
bin/dash-proxy:go build -o bin/ ./cmd/...names the output after the package directory, so thegit mvdid it. Only thedockertarget's tag needed editing.Release and sequencing
After merge:
script/release-dash v1.1.0.0, then confirm the tag is pullable, multi-arch and public from a logged-out shell before any gem change merges.Not done: the manual upgrade rehearsal from the issue — deploying a real host on the pre-rename gem, upgrading, and confirming the ACME certificate is not re-issued. It needs a scratch host and a real certificate. The container-level evidence above covers the state adoption and the uid, but not the full volume/network/container migration, which is gem-side anyway.