Skip to content

Core Deployment

The editor and its relay - everything needed for people to draw, save to their browsers and files, and edit together in live sessions. Nothing is stored on your server: documents live in each person's browser.

If you want shared team storage and sign-in as well, follow Deployment with Workspaces instead. You can start here and move to that later without losing anything.

You'll end up with

https://design.example.gov/          the editor
https://design.example.gov/docs/     this documentation, served by the editor
wss://design.example.gov/relay/      the relay, for live sessions

1. Before you start

You need:

  • A host with Docker and Docker Compose (v2 - the docker compose command).
  • A name for it in DNS, such as design.example.gov, pointing at the host.
  • A TLS certificate and key for that name. Browsers only allow live sessions over HTTPS. Your organization's CA, or Let's Encrypt, both work.
  • Ports 80 and 443 open to the people who will use it.

2. Create a folder with three files

bash
mkdir system-design && cd system-design
mkdir certs

Create each of these files in that folder, exactly as shown.

yaml
# System Design Editor - core deployment: the editor and its relay.
#
# One domain, one TLS proxy:
#   https://<DOMAIN>/        the editor (and its documentation at /docs/)
#   wss://<DOMAIN>/relay/    the relay, for live sessions
#
# Nothing here stores documents: they live in each person's browser.

name: system-design

services:
  editor:
    image: ghcr.io/jbraunsmajr/system-design:${VERSION:-latest}
    restart: unless-stopped
    environment:
      APP_URL: https://${DOMAIN:?Set DOMAIN in .env}/
      # The trailing slash matters: nginx serves the relay at /relay/.
      RELAY: wss://${DOMAIN}/relay/
      ICE_SERVERS: ${ICE_SERVERS:-}

  relay:
    image: ghcr.io/jbraunsmajr/system-design-relay:${VERSION:-latest}
    restart: unless-stopped

  proxy:
    image: nginx:alpine
    restart: unless-stopped
    ports:
      - '80:80'
      - '443:443'
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
      - ./certs:/etc/nginx/certs:ro
    depends_on:
      - editor
      - relay
nginx
# TLS termination for the System Design Editor.
# Mounted into the proxy container as /etc/nginx/conf.d/default.conf.

# WebSocket upgrades for the relay.
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

# Plain HTTP only redirects.
server {
    listen 80;
    server_name _;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name _;

    # Your certificate and key, in ./certs beside this file.
    ssl_certificate     /etc/nginx/certs/fullchain.pem;
    ssl_certificate_key /etc/nginx/certs/privkey.pem;

    # The relay. The trailing slashes strip /relay/ before it reaches the
    # relay, so /relay/health is the relay's /health.
    location /relay/ {
        proxy_pass http://relay:4444/;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_set_header Host $host;
        # Sessions stay open while people work; don't cut them off.
        proxy_read_timeout 1h;
        proxy_send_timeout 1h;
    }

    # The editor, and its documentation at /docs/.
    location / {
        proxy_pass http://editor:80;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}
ini
# The name people will type to reach the editor. Point its DNS at this host.
DOMAIN=design.example.gov

# Which release to run. "latest" follows each release; a date such as
# 2026-09-21 pins one. The three images are released together.
VERSION=latest

# Only for networks where browsers cannot reach each other directly
# (air-gapped, or strict firewalls). See "Networks without direct paths".
ICE_SERVERS=

The file is named .env

With the leading dot. Docker Compose reads it automatically from the folder you run it in.

3. Set your domain

Edit .env and set DOMAIN to your name:

ini
DOMAIN=design.example.gov

That is the only required change. Leave VERSION=latest to follow releases, or set a release date such as 2026-09-21 to pin one.

4. Add your certificate

Copy your certificate and key into certs/, named:

certs/fullchain.pem    the certificate, followed by any intermediates
certs/privkey.pem      its private key
Just trying it out? A self-signed certificate
bash
openssl req -x509 -newkey rsa:2048 -nodes -days 30 \
  -keyout certs/privkey.pem -out certs/fullchain.pem \
  -subj "/CN=design.example.gov"

Browsers will warn about it, and live sessions may refuse to connect until you accept it. Use a real certificate for anything people depend on.

5. Start it

bash
docker compose up -d

The first start downloads the images. After that it takes seconds.

6. Check it works

bash
curl https://design.example.gov/relay/health
# {"status":"ok","authentication":"none","rooms":0}

Then open https://design.example.gov in a browser. You should see the editor; the book icon in the toolbar opens this documentation from your own server.

To check live sessions, open the editor in two browsers, start a session in one (Collaborate → Start a new session), and open its link in the other.

Keeping it running

TaskCommand
See what is runningdocker compose ps
Read the logsdocker compose logs -f
Update to the newest releasedocker compose pull && docker compose up -d
Stop everythingdocker compose down

There is nothing to back up: this deployment stores no documents.

If something goes wrong

The page loads but live sessions never connect. The browser reached the editor but not the relay. Check curl https://<your domain>/relay/health answers; if it does, the network between the people in the session may be blocking direct connections - see Networks without direct paths.

nginx will not start. docker compose logs proxy names the line. Usually it's the certificate: both files must exist in certs/ with those names.

The browser warns about the certificate. The certificate does not match DOMAIN, or it is self-signed.

System Design Editor Documentation