Skip to content
NAVIDIFF DOCUMENTATION

Deploying (Railway)

Navidiff ships as two Railway services from one repo — the app (the bun + Elysia server that also serves the SPA) and the docs (this VitePress site) — plus a managed Postgres. Both build from a Dockerfile already in the repo.

1. Postgres

Add the PostgreSQL plugin to your Railway project. It exposes a DATABASE_URL; the app reads it (or NAVIDIFF_DATABASE_URL if you set one). The schema — app tables and Better Auth's — is created automatically on first boot, so there's no migration step to run.

2. App service

Point a service at the repo's root Dockerfile (single process and /data volume). Navidiff reads Railway's injected PORT; NAVIDIFF_PORT is only a local/container fallback.

  • Volume: mount one at /data — it holds events.jsonl (the verdict log, which has no upstream to refetch from) and the on-demand repo clones.
  • Reference DATABASE_URL from the Postgres plugin into NAVIDIFF_DATABASE_URL.
  • Port: Navidiff binds to Railway's injected PORT. Add the domain after the first successful deployment so Railway detects the bound port, or set the domain's target port to the value Railway assigns (currently 8080 for this service).
  • Custom domain: add yours (e.g. app.navidiff.com).

Environment

varwhat
NAVIDIFF_DATABASE_URLreference the Postgres plugin's DATABASE_URL
ANTHROPIC_API_KEYthe Claude Agent SDK's key (no host ~/.claude on Railway)
GITHUB_APP_ID, GITHUB_APP_PRIVATE_KEY, GITHUB_APP_SLUGthe GitHub App that reads PRs and powers the in-app installation flow
GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRETthe GitHub App's Client ID/secret for “Continue with GitHub” (below)
BETTER_AUTH_SECRETa random 32+ char secret — openssl rand -base64 32
BETTER_AUTH_URLthe app's public origin, e.g. https://app.navidiff.com
NAVIDIFF_OWNER_LOGINS, NAVIDIFF_OWNER_EMAILSoptional explicit bootstrap owners on a fresh deployment

3. Docs service

Add a second service pointing at the same repository and configure it as an isolated monorepo service:

  • Root Directory: /site
  • Railway Config File: /site/railway.json (the config path is absolute; it does not follow the root directory)

That builds this VitePress site with its own Dockerfile; Caddy serves it on Railway's $PORT. Attach docs.navidiff.com as its documentation domain. If this service also hosts the marketing homepage, attach navidiff.com as a second custom domain. Then point the app's "Open the app" link at the app domain.

For a direct CLI deploy of just the docs directory, run this from site/:

bash
railway up . --path-as-root --service navidiff-docs --environment production

GitHub account + App connection

On the GitHub App's settings page, set the Callback URL to:

https://app.navidiff.com/api/auth/callback/github

Copy that App's Client ID and client secret into GITHUB_CLIENT_ID / GITHUB_CLIENT_SECRET, and set GITHUB_APP_SLUG. Also set the GitHub App's Setup URL to:

https://app.navidiff.com/github/installed

Now the login wall offers Continue with GitHub. Once signed in, Workspace lets the user link a password account to GitHub and install the App on exactly the account and repositories they choose.

Access model

Password accounts are always available; GitHub is an optional second sign-in method. Access is organization-based (Better Auth's org plugin):

  • The first account created on a fresh deployment becomes the owner of the default organization. NAVIDIFF_OWNER_LOGINS and NAVIDIFF_OWNER_EMAILS provide additional explicit bootstrap owners. Everyone after the first account needs an invitation.
  • Owners and admins invite members by email and assign owner / admin / member roles and teams from the in-app Organization panel (bottom-left when signed in).
  • A signed-in user who isn't in any org sees an invite-pending screen, not the app.

Locally, password accounts work without GitHub OAuth. Set NAVIDIFF_AUTH_DISABLED=1 only for a disposable demo that deliberately bypasses the login wall.

Ready to review a real change?

Open Navidiff