Add a setup guide for the Pi, and a compose file its .env selects

docs/PI.md goes from a Pi with Docker to a working claude.ai connector:
configuration, the first token, Caddy, DNS, the VPS forwarding raw TCP,
checks from outside, connecting the clients, the monthly token, updates,
backups and troubleshooting.

docker-compose.override.yml is tracked, so Compose would have merged it on
the Pi as well — publishing ports and switching the crawl timer off. The
Pi's .env sets COMPOSE_FILE to add deploy/docker-compose.pi.yml instead,
which joins the existing Caddy network by name and builds DATABASE_URL, so
nothing tracked needs editing there. Postgres moves to a private network
shared only with the server.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-09-16 20:19:17 +02:00
parent ab265b5b0c
commit bac91303bf
7 changed files with 460 additions and 54 deletions

View File

@@ -1,5 +1,8 @@
# Deployment
This page explains the moving parts and why each is there. For the steps in
order, on the Pi, follow [PI.md](PI.md).
## The shape of it
```
@@ -35,15 +38,11 @@ this server's bearer check.
## First deploy
[PI.md](PI.md) steps 25: clone to `/opt/schulcloud-mcp`, write `.env` (the Pi's
`COMPOSE_FILE`, `CADDY_NETWORK` and `POSTGRES_PASSWORD`, generated secrets, the
first Schulcloud token), then:
```bash
git clone <this repo> /opt/schulcloud-mcp
cd /opt/schulcloud-mcp
cp .env.example .env
# Fill in TSC_URL and TSC_JWT_COOKIE (see docs/AUTH.md), then:
openssl rand -hex 32 # → MCP_AUTH_TOKEN
openssl rand -hex 32 # → MCP_PATH_SECRET, only for claude.ai (see "Connecting Claude")
docker compose up -d --build
docker compose logs -f schulcloud-mcp
```
@@ -51,7 +50,7 @@ docker compose logs -f schulcloud-mcp
Expect:
```
[schulcloud-mcp] listening on 0.0.0.0:8080 — instance https://… , auth enabled, keepalive every 30min
[schulcloud-mcp] listening on 0.0.0.0:8080 — instance https://… , auth enabled (plus secret MCP path), token from environment, 29 day(s) left, keepalive every 30min, index every 6h
```
`auth DISABLED` there means `MCP_AUTH_TOKEN` is empty — fix it before exposing
@@ -61,39 +60,29 @@ will run but every tool will fail.
## Joining the existing Caddy
The Pi already runs Caddy and PostgreSQL in a Compose project. This server needs
neither a database nor its own Caddy — only a network it shares with the
existing one.
The Pi already runs Caddy in a container. This server needs no Caddy of its own
— only to sit on the same Docker network, so Caddy reaches it by name and no
host port is published.
Find the network Caddy is on:
```bash
docker inspect -f '{{range $k,$v := .NetworkSettings.Networks}}{{$k}}{{"\n"}}{{end}}' <caddy-container>
```
Then in `docker-compose.yml`, set that name and mark it external:
```yaml
networks:
caddy:
external: true
name: <the network name you just found>
```
That is what `deploy/docker-compose.pi.yml` does, and the Pi's `.env` selects it
with `COMPOSE_FILE=docker-compose.yml:deploy/docker-compose.pi.yml`, so plain
`docker compose` commands pick it up and nothing tracked needs editing. It
declares the Caddy network external under the name in `CADDY_NETWORK`. Setting
`COMPOSE_FILE` also matters for what it leaves out: without it, Compose merges
`docker-compose.override.yml`, which is for local development and publishes
ports.
### Postgres
The index needs a database. On the Pi, use the existing PostgreSQL rather than
the container in `docker-compose.yml` — create a database and user for it:
The index gets its own Postgres container, on a network shared with this server
and nothing else — not with Caddy, and not with whatever else is on Caddy's
network. The Pi file builds `DATABASE_URL` from `POSTGRES_PASSWORD`. Migrations
run at startup; the first creates `pg_trgm`.
```sql
CREATE USER schulcloud WITH PASSWORD '';
CREATE DATABASE schulcloud OWNER schulcloud;
```
Then set `DATABASE_URL` in `.env` and delete the `postgres` service from the
compose file. Migrations run automatically at startup; `pg_trgm` is created by
the first migration, which needs the database owner to be able to
`CREATE EXTENSION`.
An existing Postgres works as well: create a database and a user that owns it
(it must be able to `CREATE EXTENSION`), point `DATABASE_URL` at it, and remove
the `postgres` service and the `depends_on` that names it. Nothing requires
sharing one.
Without `DATABASE_URL` the server still runs: search crawls live on every call
and `/api` returns `503`. The startup log says which mode it is in.