deploy: re-add mounts as optional (commented) third storage tier

The deploy scripts already handle mounts (dir creation + MOUNTS_DIR
injection); only the docs had dropped it. Re-add it as opt-in so scripts
and docs agree:

- compose: a commented '# - ${MOUNTS_DIR:-./mounts}:/app/data' line — the
  dir is auto-created and MOUNTS_DIR injected, but the mount line stays
  dev-opted (container-side path is app-specific, can't be auto-mounted)
- reframe the storage tip as 'Three tiers of storage': logs+config (ours),
  mounts (optional, injected + auto-created, mount line opt-in), named
  volumes (yours)
- re-add MOUNTS_DIR to the injected env vars (noted opt-in) and mention it
  in the local-fallback tip, paths line, and checklist as optional

Verified in-browser; mkdocs build --strict clean.

Signed-off-by: disqualifier <dev@disqualifier.me>
This commit is contained in:
2026-07-01 01:39:52 -04:00
parent 8c56fd98a8
commit 08ad37eddc
+31 -19
View File
@@ -134,10 +134,11 @@ services:
volumes:
- ${LOGS_DIR:-./logs}:/app/logs # (4)!
- ${CONFIG_DIR:-./config}:/app/config # (5)!
- cache:/app/cache # (6)!
# - ${MOUNTS_DIR:-./mounts}:/app/data # (6)!
- cache:/app/cache # (7)!
volumes:
cache: # (7)!
cache: # (8)!
```
1. The service key is always **`svc`** — never a repo-specific name, never a
@@ -148,23 +149,32 @@ volumes:
line that stays; it's not repo-specific.
3. **Required.** The host-side service is oneshot; Docker's own restart policy is
what recovers a crashed container.
4. Logs → host, path **injected** by the deploy layer. The `:-./logs` default lets
you `docker compose up` locally with nothing set and still work.
5. Config → host, likewise injected.
6. Named volume with a **bare name** — ephemeral, auto-namespaced per service at
4. Logs → host (**output**), path **injected** by the deploy layer. The `:-./logs`
default lets you `docker compose up` locally with nothing set and still work.
5. Config → host (**input you edit**), likewise injected.
6. **Optional, commented by default.** A host dir for arbitrary read/write data.
The deploy layer injects `MOUNTS_DIR` and auto-creates the dir — but you
**uncomment** the line and pick the container path (`/app/data` here), since
that's app-specific and can't be auto-mounted.
7. Named volume with a **bare name** — ephemeral, auto-namespaced per service at
deploy time so it can't collide.
7. Declare bare (`cache`, not `myapp_cache`). Deploy auto-prefixes it.
8. Declare bare (`cache`, not `myapp_cache`). Deploy auto-prefixes it.
!!! tip "Logs and config are ours; the rest is yours"
**We handle `logs` and `config`** — the two `${...}` host mounts above are
injected and managed at deploy time. **Everything else is yours:** anything
your service needs to persist (caches, profiles, scratch) goes in a **named
volume**, which Docker owns — no host paths for you to manage.
!!! tip "Three tiers of storage"
- **`logs`** (output) and **`config`** (input you edit) — **we handle these**:
injected and managed at deploy time.
- **`mounts`** (optional) — a host dir for arbitrary read/write data. The deploy
layer **injects `MOUNTS_DIR` and auto-creates the dir**, but the mount line is
**opt-in**: uncomment it and choose the container path yourself (it's
app-specific, so it can't be auto-mounted).
- **Named volumes** (`cache`, …) — **yours**: anything else your service
persists. Docker owns them, no host paths to manage.
!!! tip "The `${VAR:-./default}` pattern"
Both host paths use a variable with a local fallback. Locally, `docker compose
up` needs nothing set — it uses `./logs` and `./config`. On the fleet, the
deploy layer sets the real values (below), so the same file works both places.
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
it). On the fleet, the deploy layer sets the real values (below), so the same
file works both places.
## How deploy fills it in
@@ -175,6 +185,7 @@ are injected — not baked into your file. You can rely on these being present:
COMPOSE_PROJECT_NAME=<workspace>-<name>
LOGS_DIR=/srv/logs/<workspace>/<name>
CONFIG_DIR=/srv/config/<workspace>/<name>
MOUNTS_DIR=/srv/mounts/<workspace>/<name> # dir auto-created; mount line opt-in
```
Which makes ownership obvious and collisions impossible:
@@ -192,9 +203,9 @@ Which makes ownership obvious and collisions impossible:
- **`<name>`** — the git repo name, lowercased, by default. Overridable at
deploy time.
Host paths live under `/srv/<kind>/<workspace>/<name>/` (config, logs), all owned by
the `services` user (uid/gid **1337**). You never write these paths in your compose —
you read the injected `${...}` variables.
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
@@ -269,7 +280,8 @@ services:
- service key `svc` — **no `container_name`**, no repo-specific names
- `user: "1337:1337"` — the shared `services` account
- `restart: unless-stopped`
- host paths via `${LOGS_DIR}` / `${CONFIG_DIR}` — never hardcoded
- host paths via `${LOGS_DIR}` / `${CONFIG_DIR}` (and optional `${MOUNTS_DIR}`) —
never hardcoded
- named volumes with **bare names** (`cache`, not `myapp_cache`)
- for browser/subprocess workloads: `init: true`, `shm_size`, `mem_limit`