You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* docs(web/guides): fix deployment guides to match verified wheels deploy behavior
Audit batch 2 (p1-16-deploy) corrections across seven deployment guides:
broken invocations replaced with the working flat aliases, env.secret
delivery marked pending (#2956/#2957), setup==deploy orchestration gap
called out (#2957), failure-hook masking documented (#3087), accessory
files:/labels corrected (#3088), and all four broken observability
examples fixed against a live Lucee 7 harness.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Peter Amiri <peter@alurium.com>
* docs(web/guides): version-qualify the docker-login stdin claim in the secrets flow
The flow diagram asserted the registry password is fed to docker login
over stdin unconditionally, but that is develop-only behavior (post-#3008).
The released 4.0.3 CLI still inlines it via -p <password>, exposing it in
the remote process table and --dry-run output. Qualify the claim to match
the 4.0.3-vs-develop split the rest of the page already uses.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Peter Amiri <peter@alurium.com>
---------
Signed-off-by: Peter Amiri <peter@alurium.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: web/sites/guides/src/content/docs/v4-0-0/deployment/accessories.mdx
+12-10Lines changed: 12 additions & 10 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -49,7 +49,7 @@ accessories:
49
49
50
50
`wheels deploy accessory boot redis` on first deploy. Produces a container named `<service>-redis` on the named host, published on 6379. From the app side, connect to `redis://192.0.2.20:6379`.
51
51
52
-
## Postgres with volume, env, and an init file
52
+
## Postgres with volumeand env
53
53
54
54
```yaml title="config/deploy.yml (illustrative — do not type)"
- `env.secret:`pulls `POSTGRES_PASSWORD` from `.kamal/secrets` — never commit it to `deploy.yml`.
68
+
- `env.clear:`values become `docker run -e` flags on the accessory container.
73
69
- `volumes:`persists `/var/lib/postgresql/data` to the host so the database survives `docker rm`.
74
-
- `files:`uploads local paths to the container filesystem at deploy time. Useful for initialization scripts, client certs, or any read-only payload the container needs.
70
+
71
+
<Aside type="caution" title="env.secret and files: are not delivered yet">
72
+
Two Kamal accessory features are parsed but not implemented in the current Phase 1 CLI:
73
+
74
+
- `env.secret:`— secret values from `.kamal/secrets` are not delivered to accessory containers ([#2956](https://github.com/wheels-dev/wheels/issues/2956)/[#2957](https://github.com/wheels-dev/wheels/issues/2957)). For something like `POSTGRES_PASSWORD` you currently have to provision the value another way (e.g. a host-managed env file or volume).
75
+
- `files:`— Kamal's "upload local paths into the container filesystem" mechanism is accepted by the config validator but no upload ever happens ([#3088](https://github.com/wheels-dev/wheels/issues/3088)). Initialization scripts and certs need to be placed on the host yourself and mounted via `volumes:`.
76
+
</Aside>
75
77
76
78
## Named containers and labels
77
79
78
-
Accessory containers are named `<service>-<accessory>` — the example above yields `myapp-db` and `myapp-redis`. They carry the same `service=` label as your app containers, so `wheels deploy details` lists them alongside everything else.
80
+
Accessory containers are named `<service>-<accessory>` — the example above yields `myapp-db` and `myapp-redis`. Their `service=` label uses that same combined value (`service=myapp-db`), **not** the app containers' bare `service=myapp` — so a `docker ps --filter label=service=myapp` won't catch them. `wheels deploy details` still lists them alongside everything else because it inspects each declared accessory container by name rather than relying on the shared label.
79
81
80
82
## Multi-host accessories
81
83
@@ -114,7 +116,7 @@ wheels deploy accessory boot all
114
116
wheels deploy accessory stop all
115
117
```
116
118
117
-
`wheels deploy setup`boots every declared accessory as part of first-run. `wheels deploy remove` tears them down.
119
+
`wheels deploy setup`does **not** boot accessories in the current Phase 1 CLI — it's an alias for `wheels deploy`, and full first-run orchestration is tracked in [#2957](https://github.com/wheels-dev/wheels/issues/2957). Run `wheels deploy accessory boot all` explicitly as part of first-run. `wheels deploy remove` does tear them down.
118
120
119
121
## Accessories and `wheels deploy`
120
122
@@ -133,6 +135,6 @@ When you do want to change an accessory — a Redis version bump, a Postgres con
Copy file name to clipboardExpand all lines: web/sites/guides/src/content/docs/v4-0-0/deployment/first-deploy.mdx
+33-17Lines changed: 33 additions & 17 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -18,7 +18,7 @@ This page takes an existing Wheels app and ships it to one or more Linux servers
18
18
- How to verify the rollout, tail logs, and roll back if something breaks
19
19
20
20
<Asidetype="note"title="Assumptions">
21
-
You have a Wheels app that already builds into a working Docker image. You have at least one Linux server you can reach over SSH as a user with passwordless `sudo`, or as root. Docker can be installed later with `wheels deploy server bootstrap` — you don't need it preinstalled. You have a container registry (Docker Hub, GHCR, ECR, or self-hosted) you can push to.
21
+
You have a Wheels app that already builds into a working Docker image. You have at least one Linux server you can reach over SSH as a user with passwordless `sudo`, or as root. Docker can be installed later with `wheels deploy bootstrap` — you don't need it preinstalled. You have a container registry (Docker Hub, GHCR, ECR, or self-hosted) you can push to.
22
22
</Aside>
23
23
24
24
## Before you start
@@ -27,7 +27,7 @@ Three quick checks before running anything:
27
27
28
28
-**SSH works.**`ssh deploy@your-host "uname -a"` succeeds without a password prompt.
-**Your app has a `Dockerfile`.** It builds and runs locally (`docker build . && docker run <image>`). If not, write the `Dockerfile`first — `wheels deploy` doesn't generate one.
30
+
-**Your app has a working `Dockerfile`.** It builds and runs locally (`docker build . && docker run <image>`). If you don't have one yet, `wheels deploy init` generates a starter `Dockerfile`in step 1 (and refuses to overwrite an existing one unless you pass `--force`) — but verify the generated one actually builds your app before deploying.
31
31
32
32
## Walk-through
33
33
@@ -41,10 +41,12 @@ Three quick checks before running anything:
41
41
wheels deploy init
42
42
```
43
43
44
-
Creates two files:
44
+
Creates four files:
45
45
46
46
-`config/deploy.yml` — the deploy manifest. Under git.
47
47
-`.kamal/secrets` — secret values. **Never under git.** Add `.kamal/secrets` to `.gitignore` immediately.
48
+
-`Dockerfile` — a starter production Dockerfile. If one already exists, `init` refuses to overwrite it unless you pass `--force`.
49
+
-`.dockerignore` — keeps build context lean.
48
50
49
51
A `.kamal/hooks/` directory is also created for optional local hook scripts. It's empty and safe to leave that way.
50
52
@@ -88,7 +90,8 @@ Three quick checks before running anything:
88
90
# Registry — matches registry.password[0] in deploy.yml
@@ -106,30 +109,43 @@ Three quick checks before running anything:
106
109
clear:
107
110
WHEELS_ENV: production
108
111
WHEELS_DATASOURCE_CLASS: com.mysql.cj.jdbc.Driver
109
-
secret:
110
-
- DATABASE_URL
111
-
- WHEELS_RELOAD_PASSWORD
112
112
```
113
113
114
-
`clear:`values are baked into `deploy.yml` and safe under git. `secret:` names pull from `.kamal/secrets`. At deploy time, `wheels deploy` translates both into `docker run -e` flags.
114
+
`clear:`values are baked into `deploy.yml` and safe under git. At deploy time, `wheels deploy` translates them into `docker run -e` flags.
115
+
116
+
<Aside type="caution" title="env.secret is not yet supported">
117
+
Kamal's `env.secret:` block — naming `.kamal/secrets` keys to be passed into the app container — is **not delivered to containers yet**. The released 4.0.3 CLI silently drops `env.secret` entries; CLIs built from `develop` reject the block outright with `Wheels.Deploy.EnvSecretUnsupported` so the gap can't bite silently. Secret delivery to app containers is tracked in [#2956](https://github.com/wheels-dev/wheels/issues/2956) and [#2957](https://github.com/wheels-dev/wheels/issues/2957). Until it lands, get secrets into the container another way (bake non-secrets into the image, or mount an env file you manage yourself). `.kamal/secrets` still works for its other consumer: `registry.password` resolution at `docker login` time.
118
+
</Aside>
115
119
116
120
5. **Bootstrap Docker on the host (first time only).**
117
121
118
122
If Docker is already installed on the target, skip this step. Otherwise:
119
123
120
-
```bash {test:cli cmd="wheels deploy server bootstrap"}
121
-
wheels deploy server bootstrap
124
+
```bash title="Install Docker on every host (requires reachable hosts)"
125
+
wheels deploy bootstrap
122
126
```
123
127
124
128
Runs `which docker || curl -fsSL https://get.docker.com | sh` on every host. Idempotent — safe to run on a host that already has Docker. Fails fast if SSH or `sudo` isn't configured correctly.
125
129
130
+
<Aside type="note" title="Use the flat verb">
131
+
Use `wheels deploy bootstrap`, not `wheels deploy server bootstrap`. The CLI runtime registers its own top-level `server` subcommand (Lucee instance management), so the nested form prints that unrelated help text and never reaches the deploy module ([#2677](https://github.com/wheels-dev/wheels/issues/2677)).
132
+
</Aside>
133
+
126
134
6. **Run setup once.**
127
135
128
136
```bash title="First-time deploy"
129
137
wheels deploy setup
130
138
```
131
139
132
-
`setup`is the first-run verb. It runs the full deploy flow *and* boots `kamal-proxy` and any accessories you've declared. On subsequent deploys you use `wheels deploy` (without `setup`) because the proxy and accessories are already running.
140
+
`setup`is the first-run verb. In the current Phase 1 CLI it is an alias for `wheels deploy` — it does **not** yet boot `kamal-proxy` or any accessories you've declared (full first-run orchestration is tracked in [#2957](https://github.com/wheels-dev/wheels/issues/2957)). Boot those explicitly before (or right after) your first deploy:
141
+
142
+
```bash title="First-run orchestration (run explicitly for now)"
143
+
wheels deploy proxy boot
144
+
wheels deploy accessory boot all
145
+
wheels deploy setup
146
+
```
147
+
148
+
On subsequent deploys you use `wheels deploy` — the proxy and accessories are already running.
133
149
134
150
Expect a few minutes on the first run — Docker pulls the base images for `kamal-proxy`, any accessories, and your app. Output is prefixed with `[host]` so you can see what each server is doing in parallel.
135
151
@@ -140,10 +156,10 @@ Three quick checks before running anything:
140
156
Check the container state:
141
157
142
158
```bash title="Check container state"
143
-
wheels deploy app details
159
+
wheels deploy app containers
144
160
```
145
161
146
-
Prints `docker ps` output filtered by your service label, for every host. You should see one running container per host per role.
162
+
Prints `docker ps` output filtered by your service label, for every host. You should see one running container per host per role. (`wheels deploy details` gives the wider view — app, proxy, and accessories. `wheels deploy app details` is a different verb: it requires `--release=<version>` and reports a single container's `docker inspect` status.)
147
163
148
164
8. **Make a change and redeploy.**
149
165
@@ -155,7 +171,7 @@ Three quick checks before running anything:
155
171
156
172
Same command, no `setup`. The rolling flow kicks in: new image builds, pushes, pulls to every host, then the proxy cuts traffic over host-by-host. Zero downtime — `kamal-proxy` drains in-flight requests to the old container before switching.
157
173
158
-
The version label is the short git sha by default. Override with `--version=<tag>` if you tag releases differently.
174
+
The version label is the short git sha by default. Override with `--release=<tag>` if you tag releases differently. (`--release` rather than `--version` because the CLI runtime's root parser claims `--version` for itself.)
159
175
160
176
</Steps>
161
177
@@ -178,12 +194,12 @@ If something looks wrong, `wheels deploy details` is always the first stop.
178
194
Every deploy tags its container with the version label (git sha by default). To roll back, you point `wheels deploy rollback` at a previous version:
179
195
180
196
```bash title="Roll back to a previous version"
181
-
wheels deploy rollback --version=abc1234
197
+
wheels deploy rollback abc1234
182
198
```
183
199
184
-
Finds containers tagged `abc1234` on every host, starts them, and asks the proxy to switch traffic back. The old containers usually still exist on-host — `wheels deploy` doesn't prune aggressively — so rollback is fast. If you've pruned, the rollback fails at the "no such container" step; re-deploy from that sha instead.
200
+
The version is a positional argument, same as Ruby Kamal's `kamal rollback VERSION`. Don't write `--version=abc1234` — the CLI runtime's root parser intercepts `--version` before the deploy module sees it and the command fails ([#2674](https://github.com/wheels-dev/wheels/issues/2674)).
185
201
186
-
Scoped rollouts and rollbacks are supported via `--hosts=` and `--role=` flags, useful when you want to test a change on one host before fanning out.
202
+
Finds containers tagged `abc1234` on every host, starts them, and asks the proxy to switch traffic back. The old containers usually still exist on-host — `wheels deploy` doesn't prune aggressively — so rollback is fast. If you've pruned, the rollback fails at the "no such container" step; re-deploy from that sha instead.
Copy file name to clipboardExpand all lines: web/sites/guides/src/content/docs/v4-0-0/deployment/hooks.mdx
+8-8Lines changed: 8 additions & 8 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -27,20 +27,19 @@ You've read [Your First Deploy](/v4-0-0/deployment/first-deploy/). Hooks are use
27
27
|------|-------|---------|
28
28
|`pre-deploy`| Before any host work starts. | Yes — non-zero exit aborts the deploy before anything on the servers changes. |
29
29
|`post-deploy`| After a successful deploy. | Yes — non-zero exit fails the deploy after-the-fact, useful for smoke tests. |
30
-
|`post-deploy-failure`| After a deploy that threw an error. |No — this is already a failure path; the exit code is logged but doesn't change what happens. |
30
+
|`post-deploy-failure`| After a deploy that threw an error. |Already a failure path — but make the hook itself exit 0. A non-zero exit currently throws and **replaces the original deploy error** in the output, masking the root cause ([#3087](https://github.com/wheels-dev/wheels/issues/3087)). |
31
31
32
32
All three live under `.kamal/hooks/` and must be executable (`chmod +x`). They run on the control machine — the same machine running `wheels deploy` — not on the target hosts.
33
33
34
34
## Environment variables every hook receives
35
35
36
-
`wheels deploy` fires every hook with a `KAMAL_*` env block that matches Ruby Kamal's contract exactly. Keeping the prefix `KAMAL_` (not `WHEELS_`) means any hook written for Ruby Kamal works unchanged — you can port hooks between the two tools without editing.
36
+
`wheels deploy` fires every hook with a `KAMAL_*` env block that follows Ruby Kamal's naming. Keeping the prefix `KAMAL_` (not `WHEELS_`) means hooks written for Ruby Kamal that read these variables work unchanged — you can port them between the two tools without editing. The block is a subset of Ruby Kamal's full contract: variables like `KAMAL_ROLE` and `KAMAL_SERVICE` are not set.
37
37
38
38
| Variable | When available | Value |
39
39
|----------|---------------|-------|
40
40
|`KAMAL_VERSION`| all events | Version being deployed (git short sha by default). |
41
41
|`KAMAL_HOSTS`| all events | Comma-separated list of hosts in the deploy. |
42
-
|`KAMAL_PERFORMER`| all events | Local user running `wheels deploy` (`$USER`). |
43
-
|`KAMAL_ROLE`| all events | Role being deployed, or empty when the deploy spans roles. |
42
+
|`KAMAL_PERFORMER`| all events |`git config user.name`, falling back to `$USER` when unset. |
44
43
|`KAMAL_DESTINATION`| all events | Value of `--destination`, or empty. |
45
44
|`KAMAL_RUNTIME`|`post-deploy`, `post-deploy-failure`| Seconds elapsed since the deploy started. |
46
45
|`KAMAL_ERROR`|`post-deploy-failure` only | Error message that stopped the deploy. |
@@ -83,7 +82,8 @@ The non-zero exit stops `wheels deploy` before it touches production.
83
82
84
83
```bash title=".kamal/hooks/post-deploy-failure (illustrative — do not type)"
85
84
#!/usr/bin/env bash
86
-
set -euo pipefail
85
+
# Deliberately no `set -e`: exit 0 even if the page fails, so the
86
+
# original deploy error stays visible (see #3087).
87
87
88
88
curl -sS -X POST https://events.pagerduty.com/v2/enqueue \
89
89
-H 'Content-Type: application/json' \
@@ -98,7 +98,7 @@ curl -sS -X POST https://events.pagerduty.com/v2/enqueue \
98
98
}
99
99
}
100
100
EOF
101
-
)"
101
+
)"||true
102
102
```
103
103
104
104
## Output
@@ -112,7 +112,7 @@ Hook stdout and stderr are merged and prefixed with `[hook:<name>]` in the deplo
112
112
113
113
A non-zero exit from `pre-deploy` aborts the deploy before any server work. A non-zero exit from `post-deploy` marks the deploy as failed after-the-fact — the containers are already rolled over, so this is useful for integration smoke tests that gate "did the deploy actually work" rather than "can I start the new container."
114
114
115
-
`post-deploy-failure`never changes the exit code; it runs best-effort on an already-failed path.
115
+
`post-deploy-failure`runs on an already-failed path, so the overall exit stays non-zero either way — but a non-zero exit from the hook itself currently throws and **replaces the original deploy error** in the output ([#3087](https://github.com/wheels-dev/wheels/issues/3087)). Until that's fixed, write `post-deploy-failure` hooks to always exit 0 (swallow notification failures with `|| true`) so the real error survives.
116
116
117
117
## Debugging hooks
118
118
@@ -141,7 +141,7 @@ KAMAL_PERFORMER=$USER \
141
141
<LinkCard
142
142
title="Secrets"
143
143
href="/v4-0-0/deployment/secrets/"
144
-
description="Hooks can shell out to wheels deploy secrets fetch."
144
+
description="Hooks can shell out to wheels deploy fetch-secrets."
0 commit comments