Deployment Guides

Deploy
Anywhere

Production-ready deployment patterns for Warp. Multi-region, GDPR-compliant, and battle-tested.

Fly.io with GDPR Data Residency

Featured

Deploy Warp across multiple regions with geo-aware data residency. User data stays in their home region (EU/US/APAC) while writes can happen from anywhere.

Multi-region GDPR DNS Clustering High Availability HyperMode Volumes

Docker Compose

Coming Soon

Local development and single-node production deployments with Docker Compose.

Kubernetes

Coming Soon

StatefulSets, persistent volumes, and horizontal scaling on Kubernetes.

Railway

Coming Soon

One-click deployment with Railway's managed infrastructure.

Fly.io with GDPR Data Residency

Multi-region deployment with geo-aware routing

Architecture Overview

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.
Key Principles
  • Nodes auto-discover via WARP_CLUSTER_DNS
  • User data stays in their home region
  • Shards rebalance automatically on scale
  • Entity IDs encode region: eu/user/alice
High Availability
  • Leader writes, replicas sync async
  • Fire-and-forget (no latency impact)
  • Automatic failover on leader death
  • Highest sequence wins election
Benefits
  • GDPR compliant data residency
  • Zero data loss on failover
  • Easy export and deletion per entity
  • Add more regions as needed
01

Project Setup

# Create a new Fly app
fly apps create warp-cluster

# Create volumes in each region
fly volumes create warp_data --region ams --size 10  # Amsterdam (EU)
fly volumes create warp_data --region iad --size 10  # Virginia (US)
fly volumes create warp_data --region sin --size 10  # Singapore (APAC)

# Deploy machines to each region
fly machine run . --region ams --volume warp_data:/data
fly machine run . --region iad --volume warp_data:/data
fly machine run . --region sin --volume warp_data:/data
01.5

DNS Auto-Discovery

New

Warp nodes automatically discover each other via Fly's internal DNS. No manual seed configuration needed.

# fly.toml
app = "warp-cluster"
primary_region = "iad"

[env]
  WARP_PORT = "9090"
  WARP_DATA_DIR = "/data"
  WARP_SHARDS = "16"
  # Enable DNS-based cluster discovery
  WARP_CLUSTER_DNS = "warp-cluster.internal"

[mounts]
  source = "warp_data"
  destination = "/data"

[[services]]
  internal_port = 9090
  protocol = "tcp"

  [[services.ports]]
    port = 9090
How it works

When WARP_CLUSTER_DNS is set, each Warp node queries Fly's internal DNS every 5 seconds to discover other instances. New nodes are automatically added to the cluster, and shards are rebalanced across the ring using consistent hashing. No manual intervention required.

02

Using the SDK

With DNS clustering, just use Warp.open() — nodes discover each other automatically.

import { Warp } from '@geeksquad/warp'

// Open spawns an embedded Warp server that auto-clusters via DNS
// WARP_CLUSTER_DNS is picked up from the environment (fly.toml)
const warp = await Warp.open({
  dataDir: '/data',  // Fly volume mount point
})

// That's it! The server joins the cluster automatically.
// Entity IDs with region prefixes route to the correct node.

// Create EU user → data stays in Amsterdam
const entity = warp.entity('eu/user/hans')
await entity.append('UserCreated', {
  name: 'Hans Schmidt',
  email: 'hans@example.de',
})

// Credit the account
await entity.append('Credited', { amount: 1000 }, { aggregate: 'Account' })

// Read state
const state = await entity.get('Account')

// Clean shutdown
await warp.close()
How routing works

Requests are routed based on entity ID prefix. eu/user/hans routes to Amsterdam, us/user/alice routes to Virginia. The cluster handles this transparently via consistent hashing — you just use the entity ID and Warp figures out where to send it.

03

Scaling Up

Add more machines and shards rebalance automatically. No code changes needed.

# Scale up — add another machine in each region
fly machine run . --region ams --volume warp_data:/data
fly machine run . --region iad --volume warp_data:/data

# The new machines:
# 1. Query DNS and discover existing nodes
# 2. Join the cluster automatically
# 3. Receive shards via live rebalancing
# 4. Start serving traffic immediately

# Scale down — remove a machine
fly machine stop <machine-id>

# Remaining nodes detect the departure and
# redistribute shards among themselves
03.5

High Availability

New

Deploy multiple machines per region for automatic failover. Replicas sync asynchronously with zero latency impact.

# Deploy 2 machines per region for HA (leader + replica)
fly scale count 2 --region ams
fly scale count 2 --region iad
fly scale count 2 --region sin

# Or deploy 3 for quorum-based leader election
fly scale count 3 --region ams

# Enable HA + HyperMode in fly.toml
[env]
  WARP_MODE = "hyper"           # ETS caching, async SQLite flush
  WARP_HA_MODE = "replicated"
  WARP_HA_REPLICAS = "2"
  WARP_CLUSTER_DNS = "${FLY_APP_NAME}.internal"
  WARP_NODE_NAME = "warp_${FLY_MACHINE_ID}@${FLY_APP_NAME}.internal"
  WARP_COOKIE = "your_secret_cookie_here"  # REQUIRED: min 16 chars
WARP_COOKIE is required (v0.1.1+)

Generate: fly secrets set WARP_COOKIE=$(openssl rand -hex 16)
Requirements: Min 16 characters, cannot be "warp_secret".
Why: Secures Erlang distribution — without it, attackers can join your cluster.

How HA works

Leader election: Highest sequence number wins. If tied, lowest node name wins (deterministic).
Replication: Fire-and-forget via BEAM's pg — no acks, no latency impact.
Failover: Replicas detect leader death, run election, winner takes over instantly.

HyperMode

ETS caching: Writes go to ETS first, flush to SQLite every 10ms.
With HA: Events replicate before ETS write for durability.
Use case: Gaming state, analytics, metrics — when speed matters most.

04

GDPR Compliance

Right to Access (Export)
const entity = warp.entity(entityId)
const events = await entity.export()
// Returns all events for this entity
Right to Erasure (Delete)
const entity = warp.entity(entityId)
await entity.delete()
// Permanently removes entity and data
Data Residency Proof
function getDataResidencyInfo(entityId: string) {
  const region = entityId.split('/')[0]
  const info = {
    eu:   { datacenter: 'Amsterdam, Netherlands', jurisdiction: 'GDPR' },
    us:   { datacenter: 'Virginia, USA',          jurisdiction: 'US Law' },
    apac: { datacenter: 'Singapore',              jurisdiction: 'PDPA' },
  }
  return info[region]
}
05

Health Monitoring

import { Warp } from '@geeksquad/warp'

const warp = await Warp.open({ dataDir: '/data' })

// Check local node health
const health = await warp.health()
console.log(health)
// {
//   overall: 'healthy',
//   cluster: { nodes: 3, shards: 16 },
//   local: { shards: 6, events: 142857 }
// }

// List all nodes in the cluster
const cluster = await warp.clusterStatus()
// { localNode: '...', nodes: ['warp@...', ...], shardCount: 16 }

Summary

Feature Implementation
Clustering WARP_CLUSTER_DNS env var (auto-discovery)
Data Residency eu/, us/, apac/ entity ID prefix
Scaling Add machines → shards rebalance automatically
High Availability WARP_HA_MODE=replicated with automatic failover
Leader Election Highest sequence wins, lowest node name tie-breaker
Replication Fire-and-forget via BEAM pg (no latency impact)
HyperMode WARP_MODE=hyper — ETS caching + async flush
Storage Per-region Fly Volumes
GDPR Export entity.export()
GDPR Delete entity.delete()
Cross-region Queries warp.query(sql) — connect per-region