Self-Hosting & Operations
Invariant is designed to be self-hosted on your own cloud infrastructure (AWS RDS, GCP Cloud SQL, Supabase, Azure Database for PostgreSQL, or bare-metal Linux servers) with PostgreSQL.
1. Prerequisites
- Node.js: v18.x, v20.x, or v22.x+
- PostgreSQL: v14+ (Postgres 16 recommended)
2. Quick Setup with Docker Compose
Spin up a PostgreSQL 16 container with a basic local healthcheck. Production credentials, TLS, backups, upgrades, monitoring, and network policy remain deployment responsibilities:
yaml
# docker-compose.yml
version: "3.8"
services:
postgres:
image: postgres:16-alpine
container_name: invariant-postgres
restart: unless-stopped
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgrespassword
POSTGRES_DB: invariant
ports:
- "5432:5432"
volumes:
- invariant_pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres -d invariant"]
interval: 5s
timeout: 5s
retries: 5
volumes:
invariant_pgdata:Start the database:
bash
docker compose up -d3. Database Schema Initialization
Invariant provides two standard patterns for provisioning the required database tables:
Pattern A: Programmatic Startup Bootstrap (Recommended for Apps)
Initialize tables automatically when your application or worker boots up:
ts
import { invariant } from "@invariant-tech/sdk";
import { PostgresRuntimeStore } from "@invariant-tech/postgres";
import { Pool } from "pg";
const pool = new Pool({
connectionString: process.env.DATABASE_URL || "postgres://postgres:postgrespassword@localhost:5432/invariant",
});
export const store = new PostgresRuntimeStore({ pool });
// Initialize DDL tables and indexes if they do not already exist
await store.initializeSchema();
export const app = invariant({
storage: store,
});Pattern B: Versioned SQL for DBA Pipelines
For environments where Flyway, Liquibase, Terraform, or another operator-owned pipeline controls DDL, execute the versioned migration shipped in the installed package:
bash
psql "$DATABASE_URL" -v ON_ERROR_STOP=1 \
-f node_modules/@invariant-tech/postgres/src/schema.sqlThe script creates and records invariant_schema_migrations in the same transaction as the schema. Pin the package version in the migration artifact, retain the applied SQL with deployment records, and use only one schema owner per environment. Do not copy a DDL block from this page into an independent migration because that copy can drift from the runtime.
See Database Schema & Data Lifecycle for the canonical table contract, migration rules, backup/restore procedure, and retention behavior.
4. Multi-Worker Primitives & Distributed Leases
The store exposes OCC and lease primitives for future horizontal coordination. The current public Beta lacks command claim/read and arbitrary-runId runnable-work attachment APIs, so the diagram below is a target architecture rather than an implementable portable worker loop. Request-driven restoration at a registered .wait() boundary is already supported through the owning Session.
text
┌────────────────────────────────────────────────────────┐
│ Shared PostgreSQL Database │
│ (workflow_executions, outbox_commands, leases) │
└───────────▲──────────────────────────────▲─────────────┘
│ │
Lease: worker_1 (active) Lease: rejected (run locked)
│ │
┌─────────┴─────────┐ ┌─────────┴─────────┐
│ Host Worker 1 │ │ Host Worker 2 │
│ (Processes Run A) │ │ (Processes Run B) │
└───────────────────┘ └───────────────────┘Storage-Layer Coordination Mechanics
- Single-Worker Run Authority: When a worker starts processing a
runId, it callsstore.acquireLease(runId, workerId, ttlMs). PostgreSQL enforces row-level locks onexecution_leases, preventing peer workers from acquiring the same run concurrently. - Incomplete Public Work-Recovery Contract: A future host can use lease expiry and runnable discovery, but portable pending-command claim and arbitrary-
runIdrunnable-work attachment are not yet exposed. This does not remove the existing Session-owned.wait()restoration path. - Atomic Commit Protection: Even if a network split causes two workers to attempt writes, Optimistic Concurrency Control (
expectedRevision) rejects the stale worker withSTALE_TRANSITION.
Do not advertise cross-process recovery with the current public Beta. The adapter persists required facts, but the portable orchestration contract is incomplete. Use the canonical Recovery Contract Matrix as the release boundary.
5. Next Steps
- Database Schema & Data Lifecycle — Operate the schema, migrations, backups, restores, and retention.
- PostgreSQL Adapter Reference — Inspect connection pooling, SSL, and store API details.
- Durability Guarantees — Review the 8 core operational guarantees.
- Execution Engine Kernel — Understand the pure state transition engine.