Nodus
Self-hosting

Production

Deploy behind real domains with automatic TLS.

The production layout uses three hostnames on one VM:

nodus.talibdar.tech        -> web:3000
relay.nodus.talibdar.tech  -> relay:8080
doc.nodus.talibdar.tech    -> docs:3001

Storage Nodes and the mobile app connect to the Relay origin. The browser loads the web app from the web origin, calls its /api/* BFF there, and opens the Relay WebSocket at NEXT_PUBLIC_RELAY_URL.

1. Point DNS at the host

Add A/AAAA records for all three hostnames. Keep them DNS-only (no proxy) so Caddy can complete Let's Encrypt HTTP-01 challenges.

2. Configure deploy/.env

WEB_HOST=nodus.example.com
RELAY_HOST=relay.nodus.example.com
DOCS_HOST=doc.nodus.example.com

PUBLIC_RELAY_URL=https://relay.nodus.example.com
NEXT_PUBLIC_RELAY_URL=https://relay.nodus.example.com
ALLOWED_ORIGINS=https://nodus.example.com
SESSION_COOKIE_SECURE=true
SESSION_COOKIE_DOMAIN=.nodus.example.com
REDIS_PASSWORD=<strong-secret>

REDIS_PASSWORD is required — Compose refuses to start without it rather than falling back to a default. It must be URL-safe (no @, :, /, #, or whitespace) because Compose interpolates it into REDIS_URL without encoding.

NEXT_PUBLIC_RELAY_URL is inlined into the browser bundle at build time. If it changes, rebuild the web image:

docker compose -f deploy/docker-compose.yml --env-file deploy/.env build web

3. Bring it up

docker compose -f deploy/docker-compose.yml --env-file deploy/.env up -d --build

Caddy obtains and renews one certificate per hostname automatically.

  • ALLOWED_ORIGINS must exactly match the browser origin (https://nodus.example.com), or the Relay rejects the WebSocket.
  • SESSION_COOKIE_DOMAIN must be the parent domain (.nodus.example.com) so the session cookie is also sent to relay.nodus.example.com.
  • SESSION_COOKIE_SECURE must be true over HTTPS.

The browser's WebSocket target comes from NEXT_PUBLIC_RELAY_URL. Leaving it empty keeps the original single-origin behavior (/ws on the web host), which is what the localhost quickstart uses.

Reverse-proxy trust

Caddy runs in front of the Relay, so TRUST_PROXY is pinned to "true" in deploy/docker-compose.yml for correct client IPs. Do not enable it when nothing trustworthy terminates the connection.

Web push

Browser push additionally needs NEXT_PUBLIC_VAPID_PUBLIC_KEY as a build arg, which must match the Relay's VAPID_PUBLIC_KEY. See Configuration.

Next

On this page