Docs

Scaling to many servers

Scale a Niral app across servers with PostgreSQL, a realtime backplane and shared job queues. Configure load balancing, sessions and health checks.

On this page 5 sections

Niral is built to run beautifully on one small box — a ₹300/month VPS, a free tier, a shared laptop. Most apps never need more. But when you outgrow a single server, Niral scales horizontally without changing your code.

The shape of a cluster

Run the same app on N servers behind a load balancer. Three things make that just work:

  • Stateless requests. Sessions live in a signed cookie by default, so any

server can serve any request — no sticky sessions, no shared session store.

  • Shared database. Point every node at the same Postgres

(NIRAL_DATABASE_URL) — managed (Neon / Supabase / RDS) or your own.

  • A real-time backplane. So publish() on one node reaches clients

connected to *another* node.

Turn on the backplane

Set two env vars and real-time channels fan out across every server using Postgres LISTEN/NOTIFY — no Redis, no extra dependency, just the pg driver Niral already ships:

NIRAL_CLUSTER=1
NIRAL_DATABASE_URL=postgres://user:pass@host:5432/db?sslmode=require

Nothing else changes. publish("alerts", msg) and client send() now reach subscribers on any node — behaviour is identical whether you run 1 server or 50. Off by default: Niral stays a single fast process until you ask for more.

Load balancer

niral deploy writes a ready deploy/nginx-cluster.conf — an nginx upstream that spreads requests across your instances (no stickiness needed):

upstream app_cluster {
    least_conn;
    server 127.0.0.1:8201;
    server 127.0.0.1:8202;
    server 127.0.0.1:8203;
    keepalive 32;
}
server {
    location / {
        proxy_pass http://app_cluster;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;   # WebSocket live channels
        proxy_set_header Connection "upgrade";
    }
}

Start the instances with the templated systemd unit (deploy/[email protected]):

sudo systemctl enable --now app@8201 app@8202 app@8203

Add more ports (or more boxes) to scale out — every instance is stateless, so there is nothing to coordinate.

Background jobs

Two options across a cluster:

  • Shared Postgres queue (symmetric). Set NIRAL_JOBS_STORE=pg on every

node — any node enqueues, any node works the queue (claimed with FOR UPDATE SKIP LOCKED), and cron fires once via a Postgres advisory-lock leader. No special box. See Jobs.

  • Dedicated worker. Or run the queue + cron on one niral jobs process

and set NIRAL_JOBS=off on the web instances.

Health checks

Every instance exposes GET /@niral/health (release + uptime, no secrets) — point your balancer's health check at it so a bad node is pulled automatically.