Skip to content

Before You Start

Four things. Three of them only you can provide.

Ubuntu 26.04 LTS is what the installer is tuned for; anything from 22.04 works. 2 vCPU, 4 GB RAM, at least 20 GB of disk.

3.7 GB of RAM is fine — the installer provisions a 4 GB swapfile. Below that, surface builds fail: a build killed with signal 137 is the box running out of memory. The node still installs and runs when a surface build fails; only that portal is skipped, and re-running the install retries it.

The 4 GB profile runs the database, auth, REST, edge functions, the gateway, storage and realtime. Studio, analytics, image proxying and the connection pooler are off by default.

You do not need to install Docker, Node.js or pnpm yourself. The install preflight does that, and configures the firewall to allow 22, 80 and 443.

A domain you control for the node — node1.example.com, say — and the ability to set an A record pointing at the box’s public IP.

Each web surface you enable needs its own A record as well (admin.node1.example.com, my.node1.example.com, and so on). The installer prints every record it needs.

Required to go live. One SMTP provider carries all of the node’s mail: sign-in links, signup confirmation and password resets from the auth service, and invitations, notifications and verification codes from the edge functions.

The installer prompts for host, port, user and password, verifies the configuration against the live provider before installing anything, and offers to send you a test message.

ProviderHostPortUserPassword
Resendsmtp.resend.com465resendyour API key
Postmarksmtp.postmarkapp.com465server tokenserver token
Your ownper provider465per providerper provider

Use port 465, which is implicit TLS. This is not a preference. On port 587 the node’s function runtime does not complete the STARTTLS upgrade. The send hangs, times out and fails, so invitations, notifications and verification codes do not go out.

Two checks pass anyway, which is what makes this hard to spot. The install check uses a different mail client and connects on 587 without trouble. Sign-in links and password resets keep arriving, because the sign-in service is a separate program with a mail client of its own — so sending yourself a sign-in link, the test the checklist asks for, succeeds on a node whose application email is broken.

Port 25 egress is blocked on most hosting providers.

The from address must be a verified domain. The node sends as noreply@<home_server> by default, and providers reject any send from a domain they have not verified for SPF and DKIM. This is not your admin_email — that is your own login and contact address, and its domain is not verified. If your node is a subdomain on an account where only the parent domain is verified, set smtp.sender_email to the verified parent. The test email at install catches this before you go live.

Without email, the node is nearly inert. Users join through email-bound invitation links, so a node that cannot send mail cannot onboard anyone. The installer also forces every federation surface off when no transport is configured, and tells you so. The only node that legitimately runs without email is one set to registration_mode: closed, which accepts no new users at all. Signups are never auto-confirmed on a node.

tc-operator runs from a complete source tree. You receive the operator distribution archive from Trusted Codes — a tarball containing the full buildable tree, with no version-control history and no internal documents.

If you were given an enrolment token, you do not fetch the archive by hand at all; the one-line installer does it for you and verifies the download against the published, signed release manifest. See Installing a node.

The federation trust root — the central registry URL and its root public key — is baked into the installer. It is one value for the whole mesh, identical on every node, and you neither provide nor change it.

Every per-node secret (the JWT secret, the anon and service keys, the Postgres password, the encryption key, the federation signing key) is generated for you into /etc/trusted-codes/secrets.env, mode 0600. You never copy a secret by hand. The one exception is your SMTP password, which you put in operator.yaml.

On a Mac or Linux workstation with Docker, you can run the whole container flow without any host provisioning or TLS:

Terminal window
scripts/tc-operator/tc-operator install --local --config operator.yaml

--local boots the node on an isolated loopback project and skips apt, the firewall, the swapfile and TLS entirely. Tear it down with tc-operator uninstall --local --config operator.yaml --purge.