deploy: scope page to the dev's job — drop operator commands

Devs never deploy — that's a central ops tool. Remove operator-facing
content and keep only what a dev is responsible for (a conforming compose).

- remove the 'Onboarding a service' section (deploy <host> <workspace>
  <git-url> command + deploy-key internals) and the 'How deploy fills it in'
  docker-ps collision table + <workspace>/<name> naming internals
- replace with 'Deployment is handled centrally': a note that devs run no
  deploy commands, plus the kept dev-facing context that the env is
  GENERATED at deploy time so ${LOGS_DIR}/${CONFIG_DIR}/${MOUNTS_DIR}
  resolve without them (less ambiguous than just 'it's handled')
- trim operator leftovers: svc-<ws>-<name> unit ref, dangling '(below)',
  and the /srv/config/<ws>/<name> path in the secrets note

Compose convention, uid 1337, restart, mounts tier, layer caching, uv,
subprocess knobs all unchanged. Verified in-browser; build --strict clean.

Signed-off-by: disqualifier <dev@disqualifier.me>
This commit is contained in:
2026-07-01 02:04:17 -04:00
parent 08ad37eddc
commit 0ebd6f86b0
+21 -51
View File
@@ -142,8 +142,7 @@ volumes:
``` ```
1. The service key is always **`svc`** — never a repo-specific name, never a 1. The service key is always **`svc`** — never a repo-specific name, never a
`container_name`. Our tooling keys off `svc` (the `svc` command, the `container_name`. The ops tooling keys off `svc`.
`svc-<workspace>-<name>` unit).
2. Run as the shared **`services`** account (**uid/gid 1337**, fixed fleet-wide). 2. Run as the shared **`services`** account (**uid/gid 1337**, fixed fleet-wide).
**No** in-container `user`/`useradd` — set it here. This is the one identity **No** in-container `user`/`useradd` — set it here. This is the one identity
line that stays; it's not repo-specific. line that stays; it's not repo-specific.
@@ -173,58 +172,29 @@ volumes:
!!! tip "The `${VAR:-./default}` pattern" !!! tip "The `${VAR:-./default}` pattern"
Host paths use a variable with a local fallback. Locally, `docker compose up` Host paths use a variable with a local fallback. Locally, `docker compose up`
needs nothing set — it uses `./logs`, `./config` (and `./mounts` if you enable needs nothing set — it uses `./logs`, `./config` (and `./mounts` if you enable
it). On the fleet, the deploy layer sets the real values (below), so the same it). When deployed, the ops tooling sets the real values, so the same file works
file works both places. both places.
## How deploy fills it in ## Deployment is handled centrally
At deploy time we set the variables your compose reads, so identity and host paths !!! note "You don't run any deploy commands"
are injected — not baked into your file. You can rely on these being present: All services are deployed centrally by the ops tooling. You don't pick a host,
manage keys, or run anything — just make your repo follow the compose convention
above and it's deployable.
At deploy time the tooling **generates** the environment your compose reads, so the
`${...}` variables resolve without anything from you. You can rely on these being
set — that's why your compose works unchanged on the fleet:
``` ```
COMPOSE_PROJECT_NAME=<workspace>-<name> LOGS_DIR # your host log dir -> ${LOGS_DIR}
LOGS_DIR=/srv/logs/<workspace>/<name> CONFIG_DIR # your host config dir -> ${CONFIG_DIR}
CONFIG_DIR=/srv/config/<workspace>/<name> MOUNTS_DIR # optional host data dir -> ${MOUNTS_DIR} (if you enable the mount)
MOUNTS_DIR=/srv/mounts/<workspace>/<name> # dir auto-created; mount line opt-in
``` ```
Which makes ownership obvious and collisions impossible: Write your logs and config to those mount points and you're set. If a service won't
come up or its logs aren't persisting, that's a host-side detail on our end — flag it
| item | value | and we'll sort it.
| --- | --- |
| container | `<workspace>-<name>-svc-1` |
| volume | `<workspace>-<name>_cache` |
| network | `<workspace>-<name>_default` |
| logs | `/srv/logs/<workspace>/<name>/` |
!!! warning "What `<workspace>` and `<name>` are"
- **`<workspace>`** — who owns it: an individual dev (`ricky`, `xattam`, …) or a
category (`tpv`, `web`, `bots`, …).
- **`<name>`** — the git repo name, lowercased, by default. Overridable at
deploy time.
Host paths live under `/srv/<kind>/<workspace>/<name>/` (config, logs, and mounts if
enabled), all owned by the `services` user (uid/gid **1337**). You never write these
paths in your compose — you read the injected `${...}` variables.
## Onboarding a service
One command registers and starts a service:
```
deploy <host> <workspace> <git-url> [name]
```
It registers the service, generates a per-repo **read-only deploy key on the host**
(the private key never leaves the box), pushes the public key to the repo's Gitea
deploy keys automatically, clones the code, and installs and starts the service's
unit. No manual key paste, no host setup on your end.
!!! note "If your service won't start or its logs aren't persisting"
That's usually a host-side bind-mount **ownership** thing — the kind of detail
**we sort out at deploy time**, not something you need to chown or provision.
If a bot won't come up or logs/caches keep vanishing, flag it and we'll fix it.
Stick to the generic `compose.yaml` above and let us handle the host.
## Layer caching ## Layer caching
@@ -297,9 +267,9 @@ services:
!!! warning "Secrets never go in the image" !!! warning "Secrets never go in the image"
We do **not** commit secrets (usually, lol). They stay **gitignored** and live We do **not** commit secrets (usually, lol). They stay **gitignored** and live
on the host under the config dir (`/srv/config/<workspace>/<name>/`), reaching on the host in your config dir, reaching your container read-only via
your container read-only via `${CONFIG_DIR}`. Add them to `.dockerignore` so a `${CONFIG_DIR}`. Add them to `.dockerignore` so a `COPY . .` can't sweep them
`COPY . .` can't sweep them into a layer. into a layer.
Rotating a secret is a host-side edit — update the file and the service picks it up Rotating a secret is a host-side edit — update the file and the service picks it up
on restart. No rebuild, and nothing you run: flag it and we handle the restart. on restart. No rebuild, and nothing you run: flag it and we handle the restart.