Apps Commands
Validate, persist, inspect, and operate applications.
All mutations are executed by the daemon through the admin API.
Without a reachable daemon the commands fail with daemon-unavailable
instead of writing locally. Mutations are idempotent: every request carries a
client-generated key, and the daemon binds that key to the exact request.
Repeating a key replays the recorded result; reusing it for a different request
is refused. An interrupted operation is never executed twice, so an ambiguous
outcome is re-queried by key before any retry (never retry under a fresh
key). Unknown apps are reported as app not found and create no state.
Local and remote targets
With no --remote/GORDON_REMOTE selected, commands discover the daemon's
owner-only administration socket. The CLI checks $XDG_RUNTIME_DIR/gordon/admin.sock
when set, then /run/user/<uid>/gordon/admin.sock, then ~/.gordon/run/admin.sock.
It accepts only a safe owner-owned socket. This works with auth.enabled=false:
the socket carries no bearer token and grants the local-owner principal only
app administration and app log reads. An explicit remote is authoritative and
never falls back to the socket.
See Local-only Mode.
gordon apps
Subcommands
| Subcommand |
Description |
apply |
Validate and persist an app manifest |
operations |
Inspect app operation journals |
list |
List applications |
show |
Show desired and active state for an app |
diff |
Show the normalized desired-vs-active diff |
secrets |
Manage app secret values |
deploy |
Activate an app revision |
restart |
Restart an app from pinned digests |
stop |
Stop an app (preserves all data) |
start |
Start a stopped app from active state |
remove |
Remove app workloads (volumes and secrets are retained) |
status |
Show effective vs observed state for an app |
logs |
Show logs for an app service |
gordon apps apply
gordon apps apply --file blog.toml [--dry-run] [--deploy] [--json]
Validates a manifest file and persists it as desired state (or dry-runs).
With --deploy, chains exactly the accepted revision into a deploy after
persistence succeeds; the two outcomes are reported separately because a
deploy may fail after the apply succeeded.
When the daemon accepts the deploy asynchronously (HTTP 202), the command
reuses the same by-key watch as gordon apps deploy: it polls the
operation journal until terminal, prints progress only when the operation
or step state changes, and never reissues the deploy. A terminal
partial/failed deploy exits nonzero while still reporting the
successful apply. Ctrl-C stops only local polling: the daemon-side
operation keeps running and the command prints how to resume it.
With --json, stdout carries exactly one final combined document after the
deploy reaches a terminal state:
{
"apply": { "app": "blog", "resulting_revision": "rev-b", "pending": true },
"deploy": { "op": "op-1", "app": "blog", "status": "success", "outcome": "success" }
}
No initial running document is emitted, and progress, transient warnings,
and Ctrl-C resume guidance go to stderr.
--dry-run and --deploy cannot be combined.
gordon apps list
gordon apps list [--json]
Lists applications with one row per app. States are applied when no revision is active, pending when desired state awaits activation, stopped when stopped intent is set, and otherwise active. A non-success last operation is appended to active in parentheses.
gordon apps show
gordon apps show APP [--json]
Shows desired revision and acceptance status, pending state, per-service
effective revision, container, digest, and restart safety, the resources the
app owns (volumes, secret paths, image references — never secret values),
stopped intent, and the last operation with its outcome.
gordon apps operations show
gordon apps operations show APP --key KEY [--json]
Recovers one operation journal entry by its client-generated request key.
--key is required. Use it to re-query an ambiguous mutation outcome before
retrying: repeating the same key replays the recorded result, while a key
reused for a different request is refused.
Flags
| Flag |
Description |
--key |
Request key (required) |
--json |
Output as JSON |
gordon apps operations watch
gordon apps operations watch APP --key KEY [--json]
Polls the operation journal by request key until the operation reaches a
terminal state, then renders the terminal journal. It never reissues the
mutation, so it is the safe way to resume after an interrupted deploy or to
follow an operation started elsewhere. Progress is printed only when the
operation or step state changes; unchanged polls are not repeated.
Exit status is nonzero for terminal partial/failed outcomes and for an
interrupted watch. Ctrl-C stops only local polling: the daemon-side
operation is not cancelled and keeps running, and the command prints the
command to resume. With --json, stdout carries exactly one final document
and progress goes to stderr.
Flags
| Flag |
Description |
--key |
Request key (required) |
--json |
Output as JSON |
gordon apps diff
gordon apps diff APP [--json]
Shows the normalized desired-vs-active diff (added, removed, changed).
gordon apps secrets
Values are accepted via KEY=VALUE arguments (discouraged: shell history) or
stdin. Names must already exist in desired or active state. Only key names are
ever echoed back — never values. Secrets are service-scoped: --service is
required for set and delete, and optional for list where it filters to
one service.
gordon apps secrets list
gordon apps secrets list APP [--service SVC] [--json]
Lists registration metadata for an app's secrets — service, key, name, source,
and presence — never secret values. --service filters to one service.
Flags
| Flag |
Description |
--service |
Filter by service |
--json |
Output as JSON |
JSON Output
[
{"service": "web", "key": "DATABASE_URL", "name": "app_database_url", "source": "desired", "presence": "unknown"}
]
gordon apps secrets set
gordon apps secrets set APP --service SVC KEY=VALUE… [--json]
gordon apps secrets set APP --service SVC --stdin [--json]
gordon apps secrets set APP --service SVC --stdin --key KEY [--json]
Reads values in one of three ways:
KEY=VALUE arguments: the value must be a single line of 1–65536 bytes.
--stdin: reads KEY=VALUE lines; blank and whitespace-only lines are
ignored and every non-blank value byte is preserved.
--stdin --key KEY: reads one raw value for KEY; a single trailing newline
is stripped and the value must still be a single non-empty line.
--key requires --stdin and cannot be combined with KEY=VALUE arguments.
An empty value is rejected, so KEY= is invalid.
Running containers keep the values they were created with. Apply new values
with gordon apps deploy APP --service SVC: deploy sees the changed secret and
recreates the service. restart does not apply new values.
Flags
| Flag |
Description |
--service |
Service the secrets belong to (required) |
--stdin |
Read KEY=VALUE lines, or one raw value with --key |
--key |
Secret key for single-value stdin mode |
--json |
Output as JSON |
JSON Output
{"app": "blog", "service": "web", "keys": ["DATABASE_URL"]}
gordon apps secrets delete
gordon apps secrets delete APP KEY --service SVC [--json]
Deletes the registered value for KEY.
Flags
| Flag |
Description |
--service |
Service the secret belongs to (required) |
--json |
Output as JSON |
gordon apps deploy
gordon apps deploy APP [--revision REV] [--service SVC | --all] [--json]
Activates a revision (default: desired head). An app with several services
needs --service NAME (one service) or --all (every service); without
either, the command fails before any change and lists the services. A
single-service app needs neither. apps apply --deploy always deploys every
service.
| Flag |
Description |
--revision REV |
Revision to activate (default: desired head) |
--service NAME |
Deploy one service only |
--all |
Deploy every service |
--json |
Output as JSON |
Deploy is the only command that applies changes. It recreates a service
when its image digest, spec, app environment, or secret values changed. A
new image behind the same tag (for example latest) has a new digest and is
deployed. A service already running with all of these unchanged keeps its
container and is reported unchanged. Services with host binds or devices
are always replaced, so bind and device policy changes apply on deploy.
Fail-fast across services:
the first failure stops the deploy, successful services are preserved,
later services stay unchanged.
When the daemon accepts the deploy and runs the two phases in the
background (HTTP 202), the command polls the operation journal by key until
it is terminal and prints concise progress only when the operation or step
state changes. It never reissues the mutation. In --json mode stdout
carries one final document and progress goes to stderr. Ctrl-C stops only
local polling (the daemon-side operation keeps running) and prints the
gordon apps operations watch command to resume.
gordon apps restart
gordon apps restart APP [--service SVC | --all] [--json]
Restarts the same container. Nothing is applied: new secret values, env,
image, or config need gordon apps deploy. Traffic is withdrawn, the
container restarts, readiness is checked, and traffic returns. If the
container no longer exists, restart rebuilds it from its pinned digest. An
app with several services needs --service NAME or --all.
gordon apps stop
gordon apps stop APP [--json]
Persists the durable stopped intent and stops exact containers.
All data (volumes, secrets) is preserved. Stopped apps stay stopped
across reboot.
gordon apps start
gordon apps start APP [--json]
Clears the stopped intent and ensures running from active state.
gordon apps remove
gordon apps remove APP [--json]
Withdraws workloads. Volumes and secrets are retained as owned orphans
under the old internal UUID; removing frees the name but never implicitly
attaches retained resources to a new app reusing the name.
There is deliberately no purge: destructive volume deletion requires a
separately accepted destructive-action contract.
gordon apps status
gordon apps status APP [--json]
Shows effective vs observed state per service.
gordon apps logs
gordon apps logs APP [--service SVC] [--follow] [--tail N] [--json]
Streams logs for a service in the app's active deployment. The app name and
--service are the only accepted identity: domains and raw container IDs are
never accepted. --service is required when the app has several services.
Workflow Example
# Push the image first (OCI transfer only)
gordon images push myapp --build --remote prod
# Apply the manifest that references the pushed tag
gordon apps apply --file blog.toml --remote prod
# Write secret values for registered names
gordon apps secrets set blog --service web DATABASE_URL=... --remote prod
# Deploy the accepted revision (polls 202 operations to terminal)
gordon apps deploy blog --remote prod
# Resume watching an interrupted or externally started operation
gordon apps operations watch blog --key <operation-key> --remote prod
# Inspect
gordon apps show blog --remote prod
gordon apps status blog --remote prod
source
1# Apps Commands
2
3Validate, persist, inspect, and operate applications.
4
5All mutations are executed by the daemon through the admin API.
6Without a reachable daemon the commands fail with `daemon-unavailable`
7instead of writing locally. Mutations are idempotent: every request carries a
8client-generated key, and the daemon binds that key to the exact request.
9Repeating a key replays the recorded result; reusing it for a different request
10is refused. An interrupted operation is never executed twice, so an ambiguous
11outcome is re-queried by key before any retry (never retry under a fresh
12key). Unknown apps are reported as `app not found` and create no state.
13
14## Local and remote targets
15
16With no `--remote`/`GORDON_REMOTE` selected, commands discover the daemon's
17owner-only administration socket. The CLI checks `$XDG_RUNTIME_DIR/gordon/admin.sock`
18when set, then `/run/user/<uid>/gordon/admin.sock`, then `~/.gordon/run/admin.sock`.
19It accepts only a safe owner-owned socket. This works with `auth.enabled=false`:
20the socket carries no bearer token and grants the `local-owner` principal only
21app administration and app log reads. An explicit remote is authoritative and
22never falls back to the socket.
23See [Local-only Mode](../config/auth.md#local-only-mode).
24
25## gordon apps
26
27### Subcommands
28
29| Subcommand | Description |
30|------------|-------------|
31| `apply` | Validate and persist an app manifest |
32| `operations` | Inspect app operation journals |
33| `list` | List applications |
34| `show` | Show desired and active state for an app |
35| `diff` | Show the normalized desired-vs-active diff |
36| `secrets` | Manage app secret values |
37| `deploy` | Activate an app revision |
38| `restart` | Restart an app from pinned digests |
39| `stop` | Stop an app (preserves all data) |
40| `start` | Start a stopped app from active state |
41| `remove` | Remove app workloads (volumes and secrets are retained) |
42| `status` | Show effective vs observed state for an app |
43| `logs` | Show logs for an app service |
44
45---
46
47## gordon apps apply
48
49```bash
50gordon apps apply --file blog.toml [--dry-run] [--deploy] [--json]
51```
52
53Validates a manifest file and persists it as desired state (or dry-runs).
54With `--deploy`, chains exactly the accepted revision into a deploy after
55persistence succeeds; the two outcomes are reported separately because a
56deploy may fail after the apply succeeded.
57
58When the daemon accepts the deploy asynchronously (HTTP 202), the command
59reuses the same by-key watch as `gordon apps deploy`: it polls the
60operation journal until terminal, prints progress only when the operation
61or step state changes, and never reissues the deploy. A terminal
62`partial`/`failed` deploy exits nonzero while still reporting the
63successful apply. Ctrl-C stops only local polling: the daemon-side
64operation keeps running and the command prints how to resume it.
65
66With `--json`, stdout carries exactly one final combined document after the
67deploy reaches a terminal state:
68
69```json
70{
71 "apply": { "app": "blog", "resulting_revision": "rev-b", "pending": true },
72 "deploy": { "op": "op-1", "app": "blog", "status": "success", "outcome": "success" }
73}
74```
75
76No initial running document is emitted, and progress, transient warnings,
77and Ctrl-C resume guidance go to stderr.
78
79`--dry-run` and `--deploy` cannot be combined.
80
81---
82
83## gordon apps list
84
85```bash
86gordon apps list [--json]
87```
88
89Lists applications with one row per app. States are `applied` when no revision is active, `pending` when desired state awaits activation, `stopped` when stopped intent is set, and otherwise `active`. A non-success last operation is appended to `active` in parentheses.
90
91---
92
93## gordon apps show
94
95```bash
96gordon apps show APP [--json]
97```
98
99Shows desired revision and acceptance status, pending state, per-service
100effective revision, container, digest, and restart safety, the resources the
101app owns (volumes, secret paths, image references — never secret values),
102stopped intent, and the last operation with its outcome.
103
104---
105
106## gordon apps operations show
107
108```bash
109gordon apps operations show APP --key KEY [--json]
110```
111
112Recovers one operation journal entry by its client-generated request key.
113`--key` is required. Use it to re-query an ambiguous mutation outcome before
114retrying: repeating the same key replays the recorded result, while a key
115reused for a different request is refused.
116
117### Flags
118
119| Flag | Description |
120|------|-------------|
121| `--key` | Request key (required) |
122| `--json` | Output as JSON |
123
124---
125
126## gordon apps operations watch
127
128```bash
129gordon apps operations watch APP --key KEY [--json]
130```
131
132Polls the operation journal by request key until the operation reaches a
133terminal state, then renders the terminal journal. It never reissues the
134mutation, so it is the safe way to resume after an interrupted deploy or to
135follow an operation started elsewhere. Progress is printed only when the
136operation or step state changes; unchanged polls are not repeated.
137
138Exit status is nonzero for terminal `partial`/`failed` outcomes and for an
139interrupted watch. Ctrl-C stops only local polling: the daemon-side
140operation is not cancelled and keeps running, and the command prints the
141command to resume. With `--json`, stdout carries exactly one final document
142and progress goes to stderr.
143
144### Flags
145
146| Flag | Description |
147|------|-------------|
148| `--key` | Request key (required) |
149| `--json` | Output as JSON |
150
151---
152
153## gordon apps diff
154
155```bash
156gordon apps diff APP [--json]
157```
158
159Shows the normalized desired-vs-active diff (`added`, `removed`, `changed`).
160
161---
162
163## gordon apps secrets
164
165Values are accepted via `KEY=VALUE` arguments (discouraged: shell history) or
166stdin. Names must already exist in desired or active state. Only key names are
167ever echoed back — never values. Secrets are service-scoped: `--service` is
168required for `set` and `delete`, and optional for `list` where it filters to
169one service.
170
171### gordon apps secrets list
172
173```bash
174gordon apps secrets list APP [--service SVC] [--json]
175```
176
177Lists registration metadata for an app's secrets — service, key, name, source,
178and presence — never secret values. `--service` filters to one service.
179
180#### Flags
181
182| Flag | Description |
183|------|-------------|
184| `--service` | Filter by service |
185| `--json` | Output as JSON |
186
187#### JSON Output
188
189```json
190[
191 {"service": "web", "key": "DATABASE_URL", "name": "app_database_url", "source": "desired", "presence": "unknown"}
192]
193```
194
195### gordon apps secrets set
196
197```bash
198gordon apps secrets set APP --service SVC KEY=VALUE… [--json]
199gordon apps secrets set APP --service SVC --stdin [--json]
200gordon apps secrets set APP --service SVC --stdin --key KEY [--json]
201```
202
203Reads values in one of three ways:
204
205- `KEY=VALUE` arguments: the value must be a single line of 1–65536 bytes.
206- `--stdin`: reads `KEY=VALUE` lines; blank and whitespace-only lines are
207 ignored and every non-blank value byte is preserved.
208- `--stdin --key KEY`: reads one raw value for `KEY`; a single trailing newline
209 is stripped and the value must still be a single non-empty line.
210
211`--key` requires `--stdin` and cannot be combined with `KEY=VALUE` arguments.
212An empty value is rejected, so `KEY=` is invalid.
213
214Running containers keep the values they were created with. Apply new values
215with `gordon apps deploy APP --service SVC`: deploy sees the changed secret and
216recreates the service. `restart` does not apply new values.
217
218#### Flags
219
220| Flag | Description |
221|------|-------------|
222| `--service` | Service the secrets belong to (required) |
223| `--stdin` | Read `KEY=VALUE` lines, or one raw value with `--key` |
224| `--key` | Secret key for single-value stdin mode |
225| `--json` | Output as JSON |
226
227#### JSON Output
228
229```json
230{"app": "blog", "service": "web", "keys": ["DATABASE_URL"]}
231```
232
233### gordon apps secrets delete
234
235```bash
236gordon apps secrets delete APP KEY --service SVC [--json]
237```
238
239Deletes the registered value for `KEY`.
240
241#### Flags
242
243| Flag | Description |
244|------|-------------|
245| `--service` | Service the secret belongs to (required) |
246| `--json` | Output as JSON |
247
248---
249
250## gordon apps deploy
251
252```bash
253gordon apps deploy APP [--revision REV] [--service SVC | --all] [--json]
254```
255
256Activates a revision (default: desired head). An app with several services
257needs `--service NAME` (one service) or `--all` (every service); without
258either, the command fails before any change and lists the services. A
259single-service app needs neither. `apps apply --deploy` always deploys every
260service.
261
262| Flag | Description |
263|------|-------------|
264| `--revision REV` | Revision to activate (default: desired head) |
265| `--service NAME` | Deploy one service only |
266| `--all` | Deploy every service |
267| `--json` | Output as JSON |
268
269Deploy is the only command that applies changes. It recreates a service
270when its image digest, spec, app environment, or secret values changed. A
271new image behind the same tag (for example `latest`) has a new digest and is
272deployed. A service already running with all of these unchanged keeps its
273container and is reported `unchanged`. Services with host binds or devices
274are always replaced, so bind and device policy changes apply on deploy.
275
276Fail-fast across services:
277the first failure stops the deploy, successful services are preserved,
278later services stay unchanged.
279
280When the daemon accepts the deploy and runs the two phases in the
281background (HTTP 202), the command polls the operation journal by key until
282it is terminal and prints concise progress only when the operation or step
283state changes. It never reissues the mutation. In `--json` mode stdout
284carries one final document and progress goes to stderr. Ctrl-C stops only
285local polling (the daemon-side operation keeps running) and prints the
286`gordon apps operations watch` command to resume.
287
288---
289
290## gordon apps restart
291
292```bash
293gordon apps restart APP [--service SVC | --all] [--json]
294```
295
296Restarts the same container. Nothing is applied: new secret values, env,
297image, or config need `gordon apps deploy`. Traffic is withdrawn, the
298container restarts, readiness is checked, and traffic returns. If the
299container no longer exists, restart rebuilds it from its pinned digest. An
300app with several services needs `--service NAME` or `--all`.
301
302---
303
304## gordon apps stop
305
306```bash
307gordon apps stop APP [--json]
308```
309
310Persists the durable stopped intent and stops exact containers.
311All data (volumes, secrets) is preserved. Stopped apps stay stopped
312across reboot.
313
314---
315
316## gordon apps start
317
318```bash
319gordon apps start APP [--json]
320```
321
322Clears the stopped intent and ensures running from active state.
323
324---
325
326## gordon apps remove
327
328```bash
329gordon apps remove APP [--json]
330```
331
332Withdraws workloads. Volumes and secrets are retained as owned orphans
333under the old internal UUID; removing frees the name but never implicitly
334attaches retained resources to a new app reusing the name.
335
336There is deliberately no `purge`: destructive volume deletion requires a
337separately accepted destructive-action contract.
338
339---
340
341## gordon apps status
342
343```bash
344gordon apps status APP [--json]
345```
346
347Shows effective vs observed state per service.
348
349---
350
351## gordon apps logs
352
353```bash
354gordon apps logs APP [--service SVC] [--follow] [--tail N] [--json]
355```
356
357Streams logs for a service in the app's active deployment. The app name and
358`--service` are the only accepted identity: domains and raw container IDs are
359never accepted. `--service` is required when the app has several services.
360
361---
362
363## Workflow Example
364
365```bash
366# Push the image first (OCI transfer only)
367gordon images push myapp --build --remote prod
368
369# Apply the manifest that references the pushed tag
370gordon apps apply --file blog.toml --remote prod
371
372# Write secret values for registered names
373gordon apps secrets set blog --service web DATABASE_URL=... --remote prod
374
375# Deploy the accepted revision (polls 202 operations to terminal)
376gordon apps deploy blog --remote prod
377
378# Resume watching an interrupted or externally started operation
379gordon apps operations watch blog --key <operation-key> --remote prod
380
381# Inspect
382gordon apps show blog --remote prod
383gordon apps status blog --remote prod
384```
385
386## Related
387
388- [CLI Overview](./index.md)
389- [Images Commands](./images.md#gordon-images-push)
390- [Deployment](../deployment/index.md)