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=pgon 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 jobsprocess
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.