Make the things that matter stand out, matching the Gitea callout look: - extra.css: brand-orange warning admonitions (#f57c00, the lambda orange), hotter danger (#e8590c), blue note/info and cyan tip/example, plus blue code-annotation markers. - deploy.md: footgun -> danger callout, secrets -> orange warning, eligible -> tip; Dockerfile and compose gain numbered code annotations explaining each magic line; paths/mounts as a table; a restart snippet for rotation. - workflow.md: warning on setting per-repo git identity before first commit; tip elevating verify-by-executing. Verified in-browser: computed border colors match (warning #f57c00, danger #e8590c), 4 annotation markers render in brand blue, admonition icons + tinted headers match the Gitea style. mkdocs build --strict clean. Signed-off-by: disqualifier <dev@disqualifier.me>
4.1 KiB
Deployment Guide
Ready for your project to see the light? You may be eligible for deployment on rethink-net — our fleet of Ubuntu 26.x servers, ready to host whatever you've built.
!!! tip "Eligible"
APIs, websites, applets, bots, monitors. The whole network runs on a few
simple, consistent rules — get your container to follow them and deploying is
mostly handing us a compose.yaml.
Docker — the services account
Every service runs containerized as the shared services account:
uid/gid 1337, fixed fleet-wide. Build your image to be uid-agnostic so it
runs cleanly as that account.
FROM python:3.12-slim
ENV HOME=/tmp # (1)!
WORKDIR /app
RUN apt-get update \
&& apt-get install -y --no-install-recommends git \
&& rm -rf /var/lib/apt/lists/* # (2)!
COPY . .
RUN pip install --no-cache-dir . \
&& chmod -R a+rwX /app # (3)!
CMD ["python", "-m", "yourapp"]
HOME=/tmp— theservicesaccount has no home dir; anything writing to$HOME(caches, configs) needs a writable target.- Install
gitonly if youpip installfrom git, then clean the apt lists to keep the image small. chmod -R a+rwX /appmakes the app tree writable by any uid — this is what "uid-agnostic" means. Note it only covers non-mounted dirs (see the footgun).
services:
yourapp:
build: .
user: "1337:1337" # (1)!
environment:
HOME: /tmp
volumes:
- /srv/configs/<project>:/app/config:ro # (2)!
- /srv/<dev>/<project>:/app/logs # (3)!
- yourapp-data:/app/data # (4)!
volumes:
yourapp-data:
- Run as the shared account. No in-container
user/useradd— don't bake a user into the image; set it here. - Configs: host-managed bind mount, mounted read-only.
- Logs: bind mount — live and rolled, scraped for monitoring.
- Everything else: a named volume. Docker owns it, so there are no host permissions to fiddle with.
Paths and mounts
| What | Where | How |
|---|---|---|
| Configs | /srv/configs/<project>/ |
bind mount, host-managed |
| Logs | /srv/<dev>/<project>/ |
bind mount; live + rolled, scraped |
| Caches, profiles, scratch | named volume | Docker manages ownership |
Permissions — the bind-mount footgun
!!! danger "Bind-mount sources must exist and be 1337-owned before up"
docker compose up does not create bind-mount directories as you. If a
bind-mount source is missing, the Docker daemon (root) creates it as
root — and your container (1337) then can't write it. Logs silently fall
back to console-only; caches re-download every run.
So:
- Bind-mount sources (configs, logs) must EXIST and be 1337-owned before
up— handled at provisioning, not a per-deploy chown hook. - Named volumes avoid this entirely — use them for anything that doesn't need host visibility.
!!! note "Why chmod a+rwX /app doesn't save you here"
The Dockerfile chmod only covers non-mounted dirs. A bind mount
overrides the image directory with the host directory, so for mounted paths
the host-side ownership wins — the image's permissions are irrelevant.
What your compose / Dockerfile needs
user: "1337:1337"- bind mounts for configs + logs
- named volumes for the rest
- secrets bind-mounted
:ro HOME=/tmpchmod -R a+rwX /appgitin the build if youpip installfrom git
Secrets
!!! warning "Secrets never go in the image"
We do not commit secrets (usually, lol). They stay gitignored, live on
the host at /srv/configs/<project>/, and are bind-mounted read-only at
runtime. Add them to .dockerignore so a COPY . . can't sweep them into a
layer.
Rotating a secret = edit the host file and restart. No rebuild.
vim /srv/configs/<project>/secrets.env # edit on the host
docker compose restart yourapp # pick up the change — no rebuild