Skip to content
Deployment

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 files

Dockerfile

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 — official nats:latest image, bound to the host’s Tailscale interface only (100.64.0.1:4222)
  • leaf — built from Dockerfile, 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:404

Run as a systemd service: cloudflared-fossil.

Update flow

# On the VPS
cd ~/EdgeSync && git pull
cd deploy && sudo docker compose up -d --build

This rebuilds the leaf container with the latest code and restarts both services. NATS does not need to restart for a leaf upgrade.

Endpoints

EndpointURLAccess
Public HTTPShttps://sync.example.comCloudflare Tunnel (anyone with the URL)
Tailscale HTTPhttp://100.64.0.1:9000Tailnet only
Tailscale NATSnats://100.64.0.1:4222Tailnet only

Mobile clients

For Expo / React Native clients (e.g. edgesync-notify-app), you’ll typically:

  1. Run a NATS leafnode on the VPS bound to port 7422 (separate from the regular 4222 so leafnode auth can be scoped)
  2. Open the leafnode port to your tailnet (ufw route allow ...)
  3. 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 --build

When 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.