Self-host the server
Run the TopGun server on your own infrastructure using Docker, Docker Compose, or Kubernetes. The server keeps its data in an embedded store (redb) inside the process — there is no database to run and no external dependency. Give it a persistent volume and your data survives restarts.
Building the image
# From the root of the repository
docker build -t topgun-server -f deploy/Dockerfile.server . The Dockerfile uses a multi-stage Rust build (rust:1.85-bookworm) and produces a minimal debian:bookworm-slim runtime image. The binary is named topgun-server and is the same binary used in production-style demos.
Docker
Run with the embedded store (default)
# Run with the embedded store (default — no database to run)
docker run -d \
--name topgun-server \
-p 8080:8080 \
-e RUST_LOG=topgun_server=info \
-v topgun-data:/data \
-e TOPGUN_DATA_DIR=/data/topgun-data \
-e TOPGUN_REDB_PATH=/data/topgun.redb \
-e TOPGUN_WAL_DIR=/data/topgun-wal \
topgun-server Docker Compose
Single node
version: '3.8'
services:
server:
build:
context: .
dockerfile: deploy/Dockerfile.server
container_name: topgun-server
ports:
- "8080:8080"
environment:
RUST_LOG: topgun_server=info
# The store directory, on the volume
TOPGUN_DATA_DIR: /data/topgun-data
# The store file of earlier versions: migrated into TOPGUN_DATA_DIR once,
# then replaced by a small marker file
TOPGUN_REDB_PATH: /data/topgun.redb
# The write-ahead log must sit on the same volume as the store directory
TOPGUN_WAL_DIR: /data/topgun-wal
# Optional: enable auth
JWT_SECRET: your-dev-secret
volumes:
- topgun-data:/data
restart: unless-stopped
volumes:
topgun-data: docker-compose up -d Backup and restore
The data is two directories on the volume: the data directory (TOPGUN_DATA_DIR, /data/topgun-data in the image) and the write-ahead log directory (TOPGUN_WAL_DIR, /data/topgun-wal in the image). Acknowledged writes that are not yet in the data directory live in the WAL directory, so a backup needs both.
- Back up: stop the server (
SIGTERM; it drains its write buffer and syncs the log, then exits 0), then copy the whole data directory recursively and the WAL directory. - Restore: with the server stopped, put both back, then start it.
- Not supported: a copy of a running server’s directory.
Upgrading a volume from an earlier version
Earlier versions kept the store as one file, /data/topgun.redb. The first start of this version migrates it into /data/topgun-data by itself, keeps the old file as /data/topgun.redb.pre-migration, and leaves a small marker file at /data/topgun.redb so that an older image refuses to start on the volume instead of serving an empty store. Copy /data/topgun.redb off the volume before upgrading, and make sure the volume has 1.5 × that file’s size plus 64 MiB free. Rolling back is only a return to the kept file and loses every write acknowledged since the upgrade; see Storage backends.
Kubernetes
For production Kubernetes deployments, use a StatefulSet so pods get stable network identities. Keep /data on a persistent volume (volumeClaimTemplates below), and store JWT_SECRET and admin credentials in a Kubernetes Secret.
Secrets
apiVersion: v1
kind: Secret
metadata:
name: topgun-secrets
namespace: topgun
type: Opaque
stringData:
jwt-secret: "your-production-secret-min-32-chars"
admin-username: "admin"
admin-password: "your-admin-password" StatefulSet + Service
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: topgun-server
namespace: topgun
spec:
serviceName: topgun-server
replicas: 1
selector:
matchLabels:
app: topgun-server
template:
metadata:
labels:
app: topgun-server
spec:
containers:
- name: topgun-server
image: topgun-server:latest
ports:
- containerPort: 8080
name: ws
env:
- name: RUST_LOG
value: "topgun_server=info"
- name: TOPGUN_BIND_ADDR
value: "0.0.0.0"
- name: TOPGUN_DATA_DIR
value: "/data/topgun-data"
- name: TOPGUN_REDB_PATH
value: "/data/topgun.redb"
- name: TOPGUN_WAL_DIR
value: "/data/topgun-wal"
- name: JWT_SECRET
valueFrom:
secretKeyRef:
name: topgun-secrets
key: jwt-secret
- name: TOPGUN_ADMIN_USERNAME
valueFrom:
secretKeyRef:
name: topgun-secrets
key: admin-username
- name: TOPGUN_ADMIN_PASSWORD
valueFrom:
secretKeyRef:
name: topgun-secrets
key: admin-password
- name: TOPGUN_MAX_RAM_MB
value: "1024"
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "2Gi"
cpu: "2"
volumeMounts:
- name: data
mountPath: /data
livenessProbe:
httpGet:
path: /health/live
port: 8080
initialDelaySeconds: 10
periodSeconds: 30
readinessProbe:
httpGet:
path: /health/ready
port: 8080
periodSeconds: 10
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 10Gi
---
apiVersion: v1
kind: Service
metadata:
name: topgun-server
namespace: topgun
spec:
selector:
app: topgun-server
ports:
- port: 8080
targetPort: 8080
name: ws
type: ClusterIP All env entries above correspond to environment variables the binary actually reads (std::env::var(...) or clap #[arg(env)]). For the complete variable list and source-file references, see Server & CLI reference.
TLS
TOPGUN_TLS_* environment variables today.The binary reads zero TOPGUN_TLS_* env vars (verified: 22 std::env::var() call sites, none matching TOPGUN_TLS). TLS is configured programmatically via TlsConfig when embedding the server in a Rust host process. See Server & CLI reference — embed API for the programmatic approach.
For standard production deployments, terminate TLS at a reverse proxy in front of the server:
- nginx —
proxy_pass http://topgun-server:8080withssl onon the nginx listener - Caddy —
reverse_proxy topgun-server:8080under atlsdirective - AWS ALB — HTTPS listener with target group pointing at port 8080; ALB handles TLS offload
WebSocket connections (ws://) are proxied as-is; the proxy upgrades wss:// externally.
Serverless
The Rust server is a long-running process that maintains in-memory CRDT state, WebSocket connections, and cluster membership. It does not support Vercel Edge Functions, AWS Lambda, or Cloudflare Workers.
Deploy TopGun as a persistent service (Docker, Kubernetes, or any platform supporting long-running processes) and connect from your edge functions using the TopGun client SDK:
import { HttpSyncProvider, SyncEngine } from '@topgunbuild/client';
import { HLC } from '@topgunbuild/core';
import { IDBAdapter } from '@topgunbuild/adapters';
const hlc = new HLC('client-1');
const provider = new HttpSyncProvider({
url: 'https://your-topgun-server.example.com',
clientId: 'client-1',
hlc,
authToken: 'your-jwt-token',
syncMaps: ['todos'],
});
const engine = new SyncEngine({
nodeId: 'client-1',
connectionProvider: provider,
storageAdapter: new IDBAdapter(),
}); HttpSyncProvider identifies itself to the client as an HTTP transport, so there is no setting to add for it; see custom connection providers in the client reference if you write a provider of your own.