Reach University Gets a Wiki — and Outline Taught Us Two Traps That Make a Failed Boot Permanent
Reach's homebase went from one card to three. Getting there cost a phantom Docker tag and a Redlock deadlock that turns one bad boot into an infinite one — both now written into the runbook so the next client's Outline is a repeat, not a re-fight.
Why Care?
Reach University had exactly one self-hosted tool: a CRM. Today it has three addresses, all under a name a human can read:
lossless.at/reach-edu/crm → Twenty
lossless.at/reach-edu/wiki → Outline ← new
lossless.at/reach-edu/postiz → Postiz ← was only reachable at a didi.sh hostOutline is the one that unlocks agents: unlike Docmost (deleted the same day it was deployed, 2026-07-27), its REST API and API keys are free in self-hosting. That was the whole reason for the switch, and Reach now has it.
What's New?
Outline deployed into the
reach-eduRailway project —outline(outlinewiki/outline:1.9.2, volume/var/lib/outline/data),outline-postgres(16, volume),outline-redis(7, volumeless). Migrations ran to completion; all four internal services (collaboration, websockets, worker, web) start clean; root URL serves 200.Reach's Postiz now answers under
lossless.at. Its*.didi.shhost isn't a leftover to clean up — Postiz derives its auth cookieDomainfromFRONTEND_URL, and*.up.railway.appis a Public Suffix apex browsers reject, so it needs a real host to function. The path model fits that exactly: pretty address in front, whatever host the tool demands behind it.
Gotchas — both make a failure permanent, not transient
The Docker tag has no
v. GitHub releases Outline asv1.9.2; Docker Hub publishes1.9.2. Deployingoutlinewiki/outline:v1.9.2fails at image pull with no log line at all — the deployment simply reads FAILED. Read the tag off Docker Hub, never the release page.A failed first boot deadlocks every boot after it. Before checking migrations, Outline takes a Redlock lock on the redis key
migrations(TTL ~180s). A container that dies holding it leaves the lock live, and the next container's retry window is shorter than the TTL — so it logsunable to achieve a quorum during its retry windowand quits. Forever. Restarting only feeds the loop. Break it: stop the service, pollredis-cli TTL migrationsuntil-2, then mint exactly one deploy.
Corrections to our own runbook
railway redeployreally does re-run the old spec — including its empty variable set. The warning indocs/outline/setup.mdstep 4 was right and got re-proven the hard way.CONFIG_EPOCHis the only reliable trigger.The redis start command is not load-bearing. The runbook prescribes
redis-server --maxmemory-policy noeviction; this deployment set none and redis still reportsnoeviction(the default when no maxmemory is set), plusprotected-mode=noandbind=* -::*. Set it for uniformity across bundles, not for correctness — and don't chase it as a cause.Tooling floor is now Railway CLI ≥5.30. On 4.8.0 the failure above is undiagnosable:
railway logs <id>returns "Deployment id does not exist" for every deployment, and there is no way to change a service image or delete a service. 5.30 addsservice source connect --image,service status, workinglogs --lines, andssh -- <cmd>. Also: the CLI's stored session token is not valid for the public GraphQL API (403) — that needs a dashboard token.
Left open
The operator gate is unchanged and still requires a human: open
lossless.at/reach-edu/wiki, complete the first-run Create workspace screen,
then wire an auth provider (Google OAuth or SMTP magic links) before the admin
session expires. palmer-ai has been stuck at exactly that step since
2026-07-27 — worth not repeating. After login, mint the API key and add
pg-dump-outline to Reach's backup crons, which today cover only Twenty.
