deploy: add COPY-into-image tabs, clarify volume ownership, drop mounts
- deploy.md: new 'Getting your files into the image' subsection — a multi-tab annotated block (single file / directory / deps-first) showing COPY <src> <dest> patterns, plus a warning not to COPY secrets. - clarify volume ownership: 'Logs and config are ours; the rest is yours' — we inject/manage the logs + config host mounts; devs put everything else in named volumes Docker owns. - remove all /srv/mounts: dropped the MOUNTS_DIR volume + env var and every mounts reference from the compose block, injected-vars, paths line, and checklist. environments.md + index.md mounts mentions cleaned too. Verified in-browser: COPY tabs switch, volume tip renders, zero mounts refs remain; mkdocs build --strict clean. Signed-off-by: disqualifier <dev@disqualifier.me>
This commit is contained in:
+54
-12
@@ -14,7 +14,7 @@
|
||||
|
||||
- service key is always **`svc`**
|
||||
- named volumes use **bare names** (`cache`, not `myapp_cache`)
|
||||
- host paths come from **`${LOGS_DIR}` / `${CONFIG_DIR}` / `${MOUNTS_DIR}`**
|
||||
- host paths come from **`${LOGS_DIR}` / `${CONFIG_DIR}`**
|
||||
|
||||
Naming things after your repo used to cause **container-name collisions** at
|
||||
fleet scale — two repos shipping the same name clashed. The generic compose
|
||||
@@ -79,6 +79,45 @@ CMD ["python", "-m", "yourapp"]
|
||||
3. `--system` installs into the image's Python (no venv needed — the container
|
||||
*is* the isolation); reads `pyproject.toml`, no `uv.lock` required.
|
||||
|
||||
### Getting your files into the image
|
||||
|
||||
`COPY . .` grabs the whole repo, but be explicit about anything that needs its own
|
||||
place — assets, templates, a config the app reads at runtime. `COPY <src> <dest>`:
|
||||
`<src>` is relative to the build context (your repo), `<dest>` is a path in the
|
||||
image.
|
||||
|
||||
=== "A single file"
|
||||
|
||||
```dockerfile
|
||||
COPY ./config.toml /app/config.toml # (1)!
|
||||
```
|
||||
|
||||
1. One file into a specific path. The app reads it at `/app/config.toml`.
|
||||
|
||||
=== "A directory"
|
||||
|
||||
```dockerfile
|
||||
COPY ./assets /app/assets # (1)!
|
||||
```
|
||||
|
||||
1. A whole tree. Trailing paths are dirs — `/app/assets/` mirrors `./assets/`.
|
||||
|
||||
=== "Deps first (layer caching)"
|
||||
|
||||
```dockerfile
|
||||
COPY requirements.txt . # (1)!
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
COPY . . # (2)!
|
||||
```
|
||||
|
||||
1. Copy just the deps file first, install, **then** copy the code.
|
||||
2. See [layer caching](#layer-caching) — this ordering is why.
|
||||
|
||||
!!! warning "Don't `COPY` secrets into the image"
|
||||
Anything sensitive stays **out** of the image — no `COPY ./secrets.env`. Secrets
|
||||
live on the host and are injected read-only at runtime (see [Secrets](#secrets)).
|
||||
Add them to `.dockerignore` so a blanket `COPY . .` can't sweep them in.
|
||||
|
||||
## Your `compose.yaml`
|
||||
|
||||
Copy this verbatim. It names nothing repo-specific — the deploy layer fills in
|
||||
@@ -95,7 +134,6 @@ services:
|
||||
volumes:
|
||||
- ${LOGS_DIR:-./logs}:/app/logs # (4)!
|
||||
- ${CONFIG_DIR:-./config}:/app/config # (5)!
|
||||
- ${MOUNTS_DIR:-./mounts}:/app/mounts
|
||||
- cache:/app/cache # (6)!
|
||||
|
||||
volumes:
|
||||
@@ -112,16 +150,21 @@ volumes:
|
||||
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. Same pattern for `${MOUNTS_DIR}`.
|
||||
5. Config → host, likewise injected.
|
||||
6. 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.
|
||||
|
||||
!!! 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 "The `${VAR:-./default}` pattern"
|
||||
Every host path uses a variable with a local fallback. Locally, `docker compose
|
||||
up` needs nothing set — it uses `./logs`, `./config`, `./mounts`. On the fleet,
|
||||
the deploy layer sets the real values (below), so the same file works both
|
||||
places.
|
||||
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.
|
||||
|
||||
## How deploy fills it in
|
||||
|
||||
@@ -132,7 +175,6 @@ 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>
|
||||
```
|
||||
|
||||
Which makes ownership obvious and collisions impossible:
|
||||
@@ -150,9 +192,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, mounts), 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), 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
|
||||
|
||||
@@ -227,7 +269,7 @@ 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}` / `${MOUNTS_DIR}` — never hardcoded
|
||||
- host paths via `${LOGS_DIR}` / `${CONFIG_DIR}` — never hardcoded
|
||||
- named volumes with **bare names** (`cache`, not `myapp_cache`)
|
||||
- for browser/subprocess workloads: `init: true`, `shm_size`, `mem_limit`
|
||||
|
||||
|
||||
@@ -68,8 +68,8 @@ happens, depending on where the project runs:
|
||||
```
|
||||
|
||||
This is how things run in production — see the [Deploy guide](deploy.md) for the
|
||||
full container standard (uid 1337, mounts, layer caching, and the uv image
|
||||
setup).
|
||||
full container standard (uid 1337, the compose convention, layer caching, and the
|
||||
uv image setup).
|
||||
|
||||
!!! tip "Which one?"
|
||||
**Local `.venv`** for quick iteration, **Makefile** when you want repeatable
|
||||
|
||||
+2
-2
@@ -70,7 +70,7 @@ out of it; examples use placeholders like `<workspace>`, `<project>`, and
|
||||
|
||||
---
|
||||
|
||||
How to get a project running on **rethink-net** — containers, paths and
|
||||
mounts, permissions, and secrets.
|
||||
How to get a project running on **rethink-net** — the compose convention,
|
||||
the one-command deploy, and secrets.
|
||||
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user