Skip to content

Self Hosting

Eric Slutz edited this page Aug 13, 2026 · 4 revisions

Self-Hosting

Self-hosting is for users with real Tandem accounts who want to run their own PumpSync-compatible backend. This page is the end-to-end walkthrough; the backend repository's docs/docker-self-host.md is the repo-local quickstart these steps stay in sync with.

Want to try PumpSync without a Tandem account first? See Demo Mode instead.

1. Run the server

You need Docker, and — to reach the server from a physical iPhone — a reverse proxy (below).

Using the published image (no build needed; it is public on GitHub Container Registry):

mkdir pumpsync && cd pumpsync
curl -fsSLO https://raw.githubusercontent.com/eslutz/PumpSync-Backend/main/.env.example
cp .env.example .env
sed -i.bak "s#^PumpSync__ServiceTokenSigningKey=.*#PumpSync__ServiceTokenSigningKey=$(openssl rand -base64 32)#" .env
rm .env.bak
mkdir -p data
docker run --detach --name pumpsync-backend \
  --publish 127.0.0.1:8080:8080 \
  --env-file .env \
  --volume "$PWD/data:/data" \
  --restart unless-stopped \
  ghcr.io/eslutz/pumpsync-backend-self-hosted:latest

Or build from source with Docker Compose:

git clone https://github.com/eslutz/PumpSync-Backend.git && cd PumpSync-Backend
cp .env.example .env
sed -i.bak "s#^PumpSync__ServiceTokenSigningKey=.*#PumpSync__ServiceTokenSigningKey=$(openssl rand -base64 32)#" .env
rm .env.bak
docker compose up --build -d

.env.example already runs against a real Tandem account by default — the only required change is the signing key, which replaces a well-known placeholder with a real random secret (the backend refuses to start with the placeholder).

2. Verify it

curl http://localhost:8080/api/v1/capabilities
curl http://localhost:8080/health

The capabilities response should include "serviceMode":"selfHosted" and "dataSourceMode":"tandemSource". The backend also logs its active mode in its first startup lines (docker logs pumpsync-backend).

3. Expose it safely (physical iPhone)

The container only speaks plain HTTP and only listens on 127.0.0.1:8080 — in self-hosted mode it issues app sessions without authentication, so it must never be directly reachable over plain HTTP from other machines. The PumpSync app also requires https:// for any address other than localhost/127.0.0.1/::1, so a phone needs HTTPS.

  • iOS Simulator on the same machine: nothing more to do; use http://localhost:8080/api in the app.
  • Physical iPhone: put a reverse proxy in front of 127.0.0.1:8080 — something that speaks HTTPS to the phone and plain HTTP to the container — then use https://your-host.example/api.

This project doesn't try to be the reverse-proxy documentation — pick a well-maintained, self-hosted option and follow its own guide:

  • Nginx Proxy Manager (recommended if you're new to this) — a web UI over nginx with a built-in Let's Encrypt client, so there's no separate certbot install or renewal cron to set up. For PumpSync: add a Proxy Host, forward it to 127.0.0.1:8080, enable "Request a new SSL certificate." See its setup guide.
  • Caddy — config-file-based, also does automatic HTTPS. See its reverse proxy quickstart.
  • Traefik — Docker-label-driven auto-discovery, a common choice if you're already running several containers under Compose.

All three are actively maintained, self-hosted (nothing routes through a third party), and equally capable of the one thing PumpSync needs.

If your proxy forwards the real client IP via X-Forwarded-For (all three above do by default), set PumpSync__TrustForwardedHeaders=true in .env so per-IP rate limiting uses the phone's real address instead of the proxy's; leave it unset otherwise.

4. Connect the app

  1. Settings → Connection: set the mode to Self-hosted, enter the server URL (including /api), tap Connect. The app creates a 12-hour session token automatically and renews it as needed.
  2. Settings → Tandem Account: enter your real Tandem username, password, and region, then Save Credentials. The backend validates them against Tandem Source and never stores them — they live only in your device's Keychain.
  3. Settings → Apple Health: allow insulin and carbohydrate write access.
  4. Sync tab: pick the initial history range and tap Start Initial Sync. Tandem Source retains roughly 14 days of history, so that is the maximum lookback.

Maintenance

  • Persistence: operational state (never credentials, tokens, or health samples) lives in data/pumpsync.db. Back it up before host or volume changes.
  • Permissions: the container runs as non-root UID 1654; on Linux, ./data must be writable by that UID (chown 1654:1654 data).
  • Upgrades: stop the container, back up data/pumpsync.db, pull the new stable latest image or an explicit numeric SemVer tag (or git pull for source builds), start again, and re-run the capabilities smoke test. latest moves only for stable backend releases; unreleased builds are available by commit-SHA tag.
  • Key rotation: changing PumpSync__ServiceTokenSigningKey invalidates outstanding app sessions; the app reconnects on its next use.
  • Data deletion: the backend repo's docs/data-deletion.md covers removing an installation's rows from your SQLite database.

Troubleshooting

  • Container exits immediately: the signing key is still the placeholder, or ./data is not writable by UID 1654. Check docker logs.
  • App says the URL is invalid: self-hosted URLs must be https:// unless the host is loopback (localhost, 127.0.0.1, ::1), and should end in /api.
  • Real Tandem credentials rejected: the backend is running in demo mode instead — the startup log and /api/v1/capabilities (dataSourceMode) both show this. Confirm you copied .env.example, not .env.demo.example.
  • "Sync limit reached": the backend rate-limits sync/validate to 12 per hour per user; wait and retry.
  • "Re-save your Tandem account credentials": Tandem rejected the stored password (for example after you changed it on tandemdiabetes.com); re-enter it under Settings → Tandem Account.

Clone this wiki locally