Skip to main content

Production Deployment

The orchestrator and main.dev.ts flow you used in Quickstart is a development tool. In production you run the node and batcher processes directly, point them at production-grade infra, and let a system service supervisor (systemd, container, k8s, etc.) keep them alive.

What changes from dev to prod

DevProd
Process supervisor@effectstream/orchestrator (bun cli.ts start)systemd / k8s / your own - do not use the orchestrator in prod
Node entrypointpackages/node/main.dev.tspackages/node/main.mainnet.ts
Batcher entrypointpackages/batcher/batcher.dev.tspackages/batcher/batcher.mainnet.ts
Configconfig.dev.ts (PgLite, local chains)config.mainnet.ts (managed PostgreSQL, real chain endpoints, deployed contract addresses)
DatabaseIn-memory PgLiteManaged PostgreSQL (RDS, Cloud SQL, self-hosted, …)
Chain infraLocal dev nodes spun up by the orchestratorReal RPCs / indexers / light nodes you provision

MQTT is currently in-process inside the node - there is no separate broker to host. The batcher is a pure MQTT client.

Running the processes

The templates already ship main.mainnet.ts and batcher.mainnet.ts. Run each as its own long-lived process:

bun run packages/node/main.mainnet.ts
bun run packages/batcher/batcher.mainnet.ts

In production both should be supervised - systemd is the simplest fit. Example unit for the node:

# /etc/systemd/system/effectstream-node.service
[Unit]
Description=Effectstream Node
After=network-online.target postgresql.service
Wants=network-online.target

[Service]
Type=simple
User=effectstream
WorkingDirectory=/opt/your-app
EnvironmentFile=/opt/your-app/.env
ExecStart=/usr/local/bin/bun run packages/node/main.mainnet.ts
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

Mirror this for the batcher (effectstream-batcher.service). Each process should run under its own systemd unit so you can restart them independently.

Database

A managed PostgreSQL instance is the only durable storage requirement. Point DB_HOST / DB_PORT / DB_USER / DB_PW / DB_NAME at it via your environment file (see Environment variables).

Schema migrations apply automatically on node startup. Both the engine's own effectstream.* tables and your app's migrations (declared in migrationTable and passed to start({ migrations })) are applied idempotently every boot - you do not run a manual migration step.

Per-chain infrastructure you must provide

Production sync requires real network access for each chain you enable. Whatever the dev orchestrator was spinning up locally, you are responsible for the production equivalent - and for deploying any application-specific contracts.

EVM (Ethereum, Arbitrum, Base, …)

  • RPC endpoint. Use a private endpoint with adequate rate limits - Alchemy, Infura, QuickNode, or a self-hosted node. Public RPCs will not survive a sync.
  • Deploy your contracts. If you extend EffectstreamL2Contract (see 105 Contracts) or use the launchpad / NFT-sale contracts, deploy them to the target network and put the resulting addresses in config.mainnet.ts.

Midnight

  • Private indexer + proof server + Midnight node. The @effectstream/npm-midnight-* packages are dev binaries; for production, run the same components as private long-lived services with their own storage and backups.
  • Deploy your Compact contracts and update the config with the resulting addresses.

Cardano

  • UTxO-RPC endpoint. All Cardano primitives stream from a UTxO-RPC source. Dolos is the recommended option for production - run a private instance. (Yaci-devkit is dev-only.)
  • See 203 Cardano for primitive-specific endpoint config.

Bitcoin

  • Bitcoin Core node, fully synced, with txindex=1 if your primitives need historical lookups. @effectstream/bitcoin-core is fine for dev; in prod run a real node.
  • ord in addition to Bitcoin Core if you use Inscriptions / Runes primitives.

Avail

  • Avail full node or light client. Use @effectstream/npm-avail-node (full) or @effectstream/npm-avail-light-client (light) as a starting point; in production run a stable long-lived instance, not the dev binary.

Celestia

  • RPC endpoint or light node. Either a Celestia light node you operate yourself, or an RPC from a provider you trust. See 209 Celestia.

NEAR

  • NEAR RPC. Use a private RPC (FastNEAR, Pagoda, or self-hosted). The bundled @effectstream/near-sandbox is dev-only.

Observability (optional)

For long-running production nodes you'll want logs and traces shipped off the host. The repo ships @effectstream/grafana-alloy and @effectstream/grafana-loki as a reference setup - point Alloy at your Loki instance and your application logs flow through automatically. Not required, but strongly recommended.