Replace the four-section dir layout with the revised flat-page spec: - Three-page nav: Libraries, Standards, Deploy. - libraries.md: live client-side fetch of the rethink-public org from the Gitea API, denylist-filtered, sorted table linking each repo, no version pins, graceful fallback on fetch/CORS failure. - standards.md: house coding standards. - deploy.md: deploy guide for rethink-net (services uid 1337, uid-agnostic Docker, paths/mounts, bind-mount permissions, read-only secrets). - index.md landing card grid updated for the three sections. - gitignore Playwright MCP artifacts. Verified: mkdocs build --strict clean; libraries fallback renders live. Signed-off-by: disqualifier <dev@disqualifier.me>
3.2 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.
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:
user: "1337:1337"in compose.chmod -R a+rwX /appin the Dockerfile (covers non-mounted dirs — see the bind-mount note below).HOME=/tmp.- No in-container
user/useradd— don't bake a user into the image.
FROM python:3.12-slim
ENV HOME=/tmp
WORKDIR /app
# git in the build if you pip-install from git
RUN apt-get update && apt-get install -y --no-install-recommends git \
&& rm -rf /var/lib/apt/lists/*
COPY . .
RUN pip install --no-cache-dir . \
&& chmod -R a+rwX /app
CMD ["python", "-m", "yourapp"]
services:
yourapp:
build: .
user: "1337:1337"
environment:
HOME: /tmp
volumes:
- /srv/configs/<project>:/app/config:ro # host-managed, read-only
- /srv/<dev>/<project>:/app/logs # live + rolled logs
- yourapp-data:/app/data # named volume — the rest
volumes:
yourapp-data:
Paths and mounts
- Configs →
/srv/configs/<project>/— bind mount, host-managed. - Logs →
/srv/<dev>/<project>/— bind mount; live and rolled, scraped for monitoring. - Everything else (caches, browser profiles, scratch) → named volumes. Docker manages ownership, so there are no host permissions to fiddle with.
Permissions — the bind-mount footgun
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 fall back to
console-only, caches re-download every run.
So:
- Bind-mount sources (configs, logs) must EXIST and be 1337-owned before
up. This is handled at provisioning, not a per-deploy chown hook. - Named volumes avoid this entirely — use them for anything that doesn't need host visibility.
- The Dockerfile
chmod -R a+rwX /apponly covers non-mounted dirs. A bind mount overrides the image directory with the host directory, so for mounted paths the host-side ownership wins.
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
We do not commit secrets (usually, lol). The rule:
- Secrets stay gitignored.
- They're placed on the host at
/srv/configs/<project>/. - They're bind-mounted read-only at runtime.
- Never baked into the image — add them to
.dockerignoresoCOPY . .can't grab them.
Rotating a secret = edit the host file and restart. No rebuild.