Self-hosting the sync server

The server is a small FastAPI + Postgres + Caddy stack. Any €4/month VPS runs it; it only shuffles ciphertext, so 2 vCPU / 4 GB is generous. Disk is the only real dimension: budget roughly 1 GB per heavy user (encrypted PDFs; experiment outputs are not synced).

Deploy in five steps

# 1. a VPS (Ubuntu 24.04) + a DNS A record: sync.yourdomain.org → VPS IP
# 2. on the VPS
apt update && apt install -y docker.io docker-compose-v2 ufw
ufw allow 22 && ufw allow 80 && ufw allow 443 && ufw --force enable

# 3. copy the server/ directory, then configure
cp .env.example .env
#   ROS_SERVER_SECRET=$(openssl rand -base64 48)
#   POSTGRES_PASSWORD=$(openssl rand -hex 24)
#   SERVER_DOMAIN=sync.yourdomain.org
#   ROS_SERVER_INVITE_CODE=…        # optional: invite-only registration

# 4. launch — Caddy fetches a Let's Encrypt certificate automatically
docker compose up -d --build

# 5. verify
curl https://sync.yourdomain.org/v1/health
A real certificate is required for the mobile app — it will not accept self-signed TLS. Public hostname + Caddy = automatic.

Invite-only registration

Set ROS_SERVER_INVITE_CODE in .env and registration requires the code — hand it to the people you invite. Leave it empty for open registration.

Operations

  • Backups: docker compose exec db pg_dump -U ros ros > backup.sql weekly, plus the blob volume. Even total server loss is survivable — every device holds plaintext.
  • Updates: copy the new server/, docker compose up -d --build.
  • Accounts: users manage themselves — passphrase change, recovery, device revocation and account deletion are all self-service in the app.