← Changelog

Third Twenty CRM Stack Deployed — This Time for Ourselves

The per-client Twenty deployment runbook just ran a third time — for The Lossless Group's own CRM. Project creation to healthy /healthz in under ten minutes, zero new failure modes.

· Michael Staton · with Claude Code on Claude Fable 5

Why Care?

We sell clients on self-hosted stacks — their own CRM, their own data, their own Railway project, no per-seat SaaS tax. If we believe that pitch, our own consulting org should run on the same shape. As of today it does: lossless is the third Twenty CRM stack deployed from docs/twenty/setup.md, after reach-edu and palmer-ai.

The quieter win is what didn't happen. The first deployment (reach-edu) was the shakedown cruise — it discovered the PORT vs NODE_PORT healthcheck trap, the seconds-not-milliseconds timeout, the ENCRYPTION_KEY migration. The second (palmer-ai) confirmed the fixes. The third ran the runbook nearly verbatim: project → five services → variables → redeploy → healthy in one pass. That's what a runbook maturing looks like.

What's New?

  • Railway project lossless in The Lossless Group workspace: twenty-server, twenty-worker, twenty-postgres (volumed), twenty-redis (noeviction), and a twenty-storage bucket — same five-part topology, same pinned image twentycrm/twenty:v2.24.1 as the other two stacks.

  • Live at https://twenty-server-production-753f.up.railway.app — healthz 200, login page verified by browser-drive. Admin account creation (Gate 1) is pending Michael's own login.

  • client-stacks/lossless/ folder of record (gitignored): stack.md, secretspec.toml, recovery .env, restore-runbook.md — mirroring the reach-edu reference shape.

  • Two deltas logged in docs/twenty/setup.md for future runs (below).

Under the Hood

Two small deltas surfaced, both tooling-shaped rather than Twenty-shaped:

  1. Image services deploy at creation, before variables exist. Creating a service from an image kicks off an immediate deploy with an empty environment. Setting variables with --skip-deploys afterward means the running deploy never sees them — every service needed an explicit railway redeploy once variables landed. Postgres is the interesting case: its first boot without POSTGRES_PASSWORD fails harmlessly, and the redeploy runs a clean initdb into the PGDATA subdir.

  2. The Railway MCP add_reference_variable tool rejects embedded references. It requires the whole value to start with ${{, so a composite like postgres://postgres:${{twenty-postgres.POSTGRES_PASSWORD}}@… doesn't pass validation. Plain set_variables accepts the template syntax and Railway resolves it fine — reference-variable wiring intact, dependency arrows and all.

Secrets discipline held: bucket credentials went CLI → file → Railway variables without ever printing to the session transcript, and the recovery copy landed in the gitignored client-stacks/lossless/twenty/.env.

What's Next

  • Gate 1: Michael creates the admin account and workspace, live.

  • Real API key (Settings → APIs, not the 2-hour playground token) → twenty-interface skill scoped to this instance.

  • Phase 3 backups: R2 bucket lossless + pg-dump-twenty single-shot cron.

  • Phase 5: a *.didi.sh subdomain via the [[custom-domain-cutover]] playbook.

Files changed

  • docs/twenty/setup.md
  • changelog/2026-07-27_01.md