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.sqlweekly, 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.