Skip to main content

Zemi

Zemi is an automatic PostgreSQL change tracking system. It connects to your database's Write-Ahead Log (WAL) and captures every data change (INSERT, UPDATE, DELETE, TRUNCATE) in real-time, persisting them to a queryable changes table.

A from-scratch rewrite of Bemi in Zig, Zemi replaces the original multi-runtime architecture (Java/Debezium + Go/NATS + TypeScript/Node.js) with a single statically-linked binary.

At a Glance

MetricOriginal BemiZemiImprovement
Docker image3.23 GB1.04 MB3,100x smaller
Memory (RSS)300-500+ MB2.8 MB~150x less
Startup time30-60 seconds<1 msinstant
Throughput~97 changes/s~2,000 changes/s~20x faster
Latency (p50)~977 ms~75 ms~13x faster
Processes4+1single process

See Benchmarks for full details.

Quick Start

Docker

docker run --rm \
-e DB_HOST=host.docker.internal \
-e DB_PORT=5432 \
-e DB_NAME=mydb \
-e DB_USER=postgres \
-e DB_PASSWORD=secret \
ghcr.io/deanmarano/zemi:latest

From Source

# Requires Zig 0.14.1 (or use: asdf install)
zig build
DB_HOST=127.0.0.1 DB_NAME=mydb DB_USER=postgres DB_PASSWORD=secret ./zig-out/bin/zemi

Pre-built Binaries

Download from GitHub Releases:

BinaryPlatformSize
zemi-x86_64-linuxLinux x86_64 (static)3.7 MB
zemi-aarch64-linuxLinux ARM64 (static)3.8 MB
zemi-x86_64-macosmacOS Intel510 KB
zemi-aarch64-macosmacOS Apple Silicon497 KB

Prerequisites

PostgreSQL 14+ with logical replication enabled:

ALTER SYSTEM SET wal_level = logical;
-- Restart PostgreSQL after this change

See the hosting platform guides for provider-specific instructions (AWS RDS, Supabase, Neon, etc.).

To track both "before" and "after" states on data changes:

ALTER TABLE [tracked_table_name] REPLICA IDENTITY FULL;

How It Works

  1. Zemi connects to PostgreSQL's logical replication stream
  2. It decodes WAL changes using the pgoutput plugin
  3. If your app uses a supported ORM package, application context (user ID, request ID, etc.) is automatically stitched onto each change
  4. Changes are persisted to a changes table in the destination database
PostgreSQL WAL --> zemi (single binary) --> PostgreSQL (changes table)

See Architecture for the full technical deep-dive.

Use Cases

  • Audit Trails -- compliance logs for customer support and external customers
  • Time Travel -- retrieve historical data without event sourcing
  • Troubleshooting -- identify root causes of application issues
  • Change Reversion -- revert changes or rollback API request side effects
  • Distributed Tracing -- track changes across distributed systems
  • Trend Analysis -- gain insights into historical data changes

Supported ORM Packages

Zemi is fully compatible with all Bemi ORM packages for automatic context stitching:

JavaScript/TypeScript

Ruby

Python

Changes Table Schema

ColumnTypeDescription
iduuidUnique change identifier
databasetextSource database name
schematextSource schema name
tabletextSource table name
operationtextINSERT, UPDATE, DELETE, or TRUNCATE
beforejsonbRow state before the change (null for INSERT)
afterjsonbRow state after the change (null for DELETE)
contextjsonbApplication context (from ORM packages)
primary_keytextPrimary key value
committed_attimestamptzTransaction commit time
positionbigintWAL position (LSN as numeric)
queued_attimestamptzTime the change was queued
created_attimestamptzTime the change was persisted

This schema is identical to the original Bemi -- existing queries work without modification.

Migrating from Bemi?

Zemi is a drop-in replacement. See the Migration Guide.