Deployment
A working production recipe for EdgeSync, derived from a reference deployment at sync.example.com. The shape: one VPS running NATS + a leaf agent, exposed publicly via Cloudflare Tunnel and privately via Tailscale.
Topology
public internet
│
▼ HTTPS
Cloudflare Tunnel ──► VPS:9000 (leaf HTTP /xfer)
│
▼
leaf agent ◄────► NATS:4222 (Tailscale-only)
│
▼
.fossil repo (volume mount)
tailnet ──────────────► VPS:9000 (HTTP)
└───► VPS:4222 (NATS)Files
The reference deployment lives in deploy/:
deploy/
├── Dockerfile # multi-stage Go build, GOWORK=off
├── docker-compose.yml # NATS on Tailscale IP, leaf on :9000
└── data/ # host volume mount with .fossil filesDockerfile
The leaf binary is built standalone (libfossil pulled from the public Go module proxy) so no private-module auth is needed. The build uses GOWORK=off because Docker copies only leaf/, not the whole workspace.
docker-compose.yml
Two containers:
nats— officialnats:latestimage, bound to the host’s Tailscale interface only (100.64.0.1:4222)leaf— built fromDockerfile, exposed on:9000
Ports 8080 and 8090 are intentionally avoided because they are occupied by Coolify and Caddy on the reference VPS. Adjust as needed.
Cloudflare Tunnel
# ~/.cloudflared/config.yml on the VPS
tunnel: <tunnel-id>
credentials-file: /home/user/.cloudflared/<tunnel-id>.json
ingress:
- hostname: sync.example.com
service: http://localhost:9000
- service: http_status:404Run as a systemd service: cloudflared-fossil.
Update flow
# On the VPS
cd ~/EdgeSync && git pull
cd deploy && sudo docker compose up -d --buildThis rebuilds the leaf container with the latest code and restarts both services. NATS does not need to restart for a leaf upgrade.
Endpoints
| Endpoint | URL | Access |
|---|---|---|
| Public HTTPS | https://sync.example.com | Cloudflare Tunnel (anyone with the URL) |
| Tailscale HTTP | http://100.64.0.1:9000 | Tailnet only |
| Tailscale NATS | nats://100.64.0.1:4222 | Tailnet only |
Mobile clients
For Expo / React Native clients (e.g. edgesync-notify-app), you’ll typically:
- Run a NATS leafnode on the VPS bound to port
7422(separate from the regular4222so leafnode auth can be scoped) - Open the leafnode port to your tailnet (
ufw route allow ...) - In the app, configure the NATS leafnode URL and pair via QR (see Notify)
Observability
Set OTEL_EXPORTER_OTLP_ENDPOINT and (if exporting to Honeycomb) the API key. EdgeSync uses Doppler to manage OTel secrets without committing .env files:
doppler run -- docker compose up -d --buildWhen the env var is unset the agent runs with a no-op observer — telemetry is zero-cost when not configured.
The reference Honeycomb dashboard (“EdgeSync Sim — Operational Overview”) tracks sync latency, convergence, file throughput, and errors across the dataset edgesync-sim in the test environment.