Skip to content

Installing Synapse on a Linux server

Synapse is a single Node.js process: the API serves the built web app itself and applies database migrations automatically at boot. A minimal install is one service plus a reverse proxy for TLS.

1. Prerequisites

  • Node.js ≥ 20.19 (22 LTS or 24 recommended) and pnpm ≥ 9 (corepack enable ships pnpm with Node).
  • Build tools for the bundled SQLite driver: apt install build-essential python3.
  • Optional, for document conversion (uploads beyond PDF):
  • pandoc + typst for markup formats (md, html, epub, tex, ...): download the static binaries from their GitHub releases and point PANDOC_PATH at pandoc (typst just needs to be on PATH).
  • LibreOffice for office formats (docx, pptx, xlsx, ...): apt install --no-install-recommends libreoffice-writer libreoffice-impress libreoffice-calc and set SOFFICE_PATH if soffice is not on PATH. Without it, office uploads are rejected with an install hint, and everything else works.
  • Optional, for AI features: an API key, configurable later from Settings. The default setup works with OpenAI's free usage tier; see AI.md.

2. Docker (the short way)

The repository ships a Dockerfile and a compose example. With Docker installed, this is the whole install:

git clone <your-fork-or-source> synapse && cd synapse
# set SESSION_SECRET in docker-compose.yml first (any long random string)
docker compose up -d --build

The app answers on http://localhost:3000; database and uploads live in named volumes. Seed the example courses once, if you want them:

docker compose exec synapse node dist/scripts/seed.js

Document conversion tools are not baked into the image to keep it small. To add them, extend the image:

FROM synapse
USER root
RUN apt-get update && apt-get install -y --no-install-recommends \
    pandoc libreoffice-writer libreoffice-impress libreoffice-calc \
    && rm -rf /var/lib/apt/lists/*
USER node

Put a TLS reverse proxy in front before exposing it beyond localhost, then continue at step 5 for accounts. The rest of this page is the manual install, without Docker.

2b. Build

git clone <your-fork-or-source> synapse && cd synapse
pnpm install
pnpm build          # builds the API (apps/api/dist) and the web app (apps/web/dist)

3. Configure

cp apps/api/.env.example apps/api/.env

The minimum for a server install:

# apps/api/.env
PORT=3000
HOST=127.0.0.1          # keep loopback; the reverse proxy is the front door
SESSION_SECRET=<paste>  # node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
DB_DIALECT=sqlite       # zero-config; or postgres + DATABASE_URL

Everything else (AUTH_*, conversion binaries, upload size, AI) is documented in .env.example and auth.md. The SQLite database lives in apps/api/data/ by default. Back up both apps/api/data/ and apps/api/uploads/: the uploads tree is the content store holding documents and sync payloads, the database holds the metadata that points into it.

Back them up in order, database first: copy the database (sqlite3 data/app.db ".backup data/backup.db" for a consistent copy while running, or dump your Postgres), then copy uploads/. That order can only leave the uploads tree ahead of the database, which is harmless (an extra file no row points at yet); the reverse could record a blob whose bytes were not copied.

SQLite is the default because a single household server does not need more. If you prefer Postgres, set DB_DIALECT=postgres and DATABASE_URL; the compose file ships a commented database service to uncomment.

4. Run as a service

# /etc/systemd/system/synapse.service
[Unit]
Description=Synapse revision app
After=network.target

[Service]
User=synapse
WorkingDirectory=/opt/synapse/apps/api
ExecStart=/usr/bin/node dist/main.js
Restart=on-failure
Environment=NODE_ENV=production

[Install]
WantedBy=multi-user.target
useradd --system --home /opt/synapse synapse   # dedicated user, no login
systemctl enable --now synapse

Migrations run automatically at startup; there is no separate migrate step. Upgrades are git pull && pnpm install && pnpm build && systemctl restart synapse.

5. TLS in front

Keep the app on loopback and terminate TLS in a reverse proxy. Caddy is the shortest path (automatic certificates):

# /etc/caddy/Caddyfile
synapse.example.org {
    reverse_proxy 127.0.0.1:3000
}

HTTPS is not optional: the session cookie is Secure in production, and the PWA install requires a secure context. For a private deployment without a public domain, Tailscale (tailscale cert) gives you HTTPS with zero exposure.

6. First run

Open the URL: Synapse shows a one-time "create the first account" screen - that account is the admin. From Settings → Users the admin creates further accounts (or set AUTH_ALLOW_SIGNUP=true for open self-registration). Each user's data is strictly isolated. On a phone, open the same URL and "Add to Home Screen", which installs it as a PWA.

The web client, with no server at all

The app also builds as a plain static site: the same local-first client the shells embed, running entirely in the browser. There is no account and no backend; the workspace lives in the browser's storage, and a server can still be connected later from Settings > Backup & sync. The build ships a small example workspace (the MIT OpenCourseWare courses), so it doubles as an online demo.

pnpm --filter @synapse/web build:web-client
# result: apps/web/dist-web-client

Deploy that folder to any static host. Cloudflare Pages and Netlify have free tiers that fit comfortably (the _redirects file included in the build gives deep links the SPA fallback both hosts understand). For GitHub Pages, copy index.html to 404.html instead.

Two limits compared to the shells: converting non-PDF uploads needs a server or a desktop install, and device-to-device LAN sync is a shell feature. To let a hosted web client connect to your server, add its origin to the server's CORS_ORIGINS.

Browser support

Synapse targets evergreen browsers (roughly 2023 and newer: Chrome 111+, Firefox 121+, Safari 16.2+). Older browsers degrade progressively: custom select styling, tinted quiz feedback and some empty-state centering fall back to plainer looks. The app works over plain http on a trusted LAN, but PWA install needs HTTPS.

See auth.md for accounts, passwords, recovery, and SSO.