Deploy Nats
Messaging system for streams, queues and pub/sub between services
nats-3
Just deployed
/data
Just deployed
/db
gateway
Just deployed
nats-1
Just deployed
/data
nats-2
Just deployed
/data
Deploy and Host NATS on Railway
NATS is a messaging system for connecting services, and its JetStream layer turns it into a durable stream and key-value store as well. Publish/subscribe, request/reply, work queues, replayable streams and a distributed KV bucket all arrive over one connection and one wire protocol, from a single Go binary — no ZooKeeper, no plugins, no JVM. It is a CNCF project with clients for over forty languages, reached for when Kafka is too heavy to operate and RabbitMQ's routing machinery is more than you need.
Deploy NATS on Railway as a three-node JetStream cluster rather than a single box. This template runs nats-1, nats-2 and nats-3 as peers of one cluster, each with its own 5 GB volume, so a stream created with three replicas keeps serving when a node restarts. Every node exposes a TCP endpoint for NATS clients, a WebSocket endpoint for browsers, and a private address other Railway services reach directly. NUI adds a browser UI for streams, buckets and live messages, behind a Caddy gateway holding the password — so you self-host NATS with authentication, persistence and replication already wired together.

Getting Started with NATS on Railway
After the deploy finishes, open the gateway service's public URL. Your browser asks for the GATEWAY_USER / GATEWAY_PASSWORD pair from the template variables — the only credential guarding the management UI. NUI then loads with your cluster already connected: the railway connection is created on first boot from the same credentials the servers use, so there is no form to fill in.
Click ALL top-left and select the railway connection. The vertical icon strip is how you move around: the top icon publishes a message, the third opens STREAMS, the fourth BUCKETS. A new cluster has neither, so create a stream — click NEW in STREAMS, name it ORDERS, subject orders.>, file storage, replicas 3. Then publish a JSON body to orders.created. Back in STREAMS, click ORDERS and open MESSAGES: your message is there with subject, payload and timestamp, proving the cluster accepts and replicates writes.
To connect an application, use the private addresses from inside Railway, or a node's TCP proxy from outside:
nats://app:PASSWORD@nats-1.railway.internal:4222,nats://app:PASSWORD@nats-2.railway.internal:4222



About Hosting NATS
NATS lets services talk without knowing where each other are. Publishers send to a subject like orders.created; subscribers ask for orders.> and get everything beneath it. Because subjects are just strings, adding a consumer never means changing a producer.
Core NATS is fire-and-forget and extremely fast. JetStream adds the durable half: a stream captures messages matching a subject pattern and holds them on disk, consumers track their own position, and a message is redelivered until acknowledged. The same subsystem backs key-value and object stores, so configuration and small blobs live alongside your events.
Key features:
- Subject-based pub/sub with wildcards, request/reply and queue groups
- JetStream durable streams with replay and at-least-once delivery
- Key-value and object stores on the same replication
- Raft clustering with per-stream replica counts
- Accounts for multi-tenant isolation, with per-account permissions
- WebSocket, MQTT and leaf-node support
The architecture has three tiers. The nats services are the cluster: they connect over the private network on port 6222, elect a Raft leader and replicate every stream you mark R3. nui talks to the cluster like any other client. gateway is a Caddy proxy holding the basic-auth credential — NUI has no login of its own, so it stays private behind it.
Why Deploy NATS on Railway
Railway removes the parts of running a cluster that are not about messaging:
- Three nodes with persistent volumes in one click
- Private networking between nodes and your apps, no VPC
- A TCP proxy per node for clients outside Railway
- Generated passwords for the app, system and cluster accounts
- Health checks and restart policies already configured
- Scaling from the dashboard
Common Use Cases
- Event-driven microservices — services publish domain events to subjects and any number of consumers react, with no service registry or HTTP fan-out.
- Durable work queues — a stream plus a pull consumer gives acknowledgements, retries and a redelivery limit, replacing Redis-and-a-library setups.
- Real-time browser updates — a frontend subscribes to subjects over WebSocket, with no separate socket tier.
Dependencies for NATS
- nats-1, nats-2, nats-3 —
nats:2.14-alpine, the official NATS Server image. Three peers of one cluster, each with a 5 GB volume at/datafor its JetStream store. - nui —
ghcr.io/nats-nui/nui:latest, an open-source NATS management GUI, with a 5 GB volume at/dbfor saved connections and message history. - gateway —
caddy:2-alpine, adding HTTP basic auth in front of NUI.
Environment Variables Reference
| Variable | Service | Purpose |
|---|---|---|
NATS_PASSWORD | all NATS nodes, nui | Password for the APP account clients use |
NATS_SYS_PASSWORD | all NATS nodes | Password for the SYS monitoring account |
NATS_CLUSTER_PASSWORD | all NATS nodes | Secret the nodes authenticate each other with |
GATEWAY_USER / GATEWAY_PASSWORD | gateway | Credentials for the management UI |
Deployment Dependencies
- github.com/nats-io/nats-server · hub.docker.com/_/nats
- github.com/nats-nui/nui · github.com/nats-io/natscli
- docs.nats.io
Hardware Requirements for Self-Hosting NATS
| Resource | Minimum | Recommended |
|---|---|---|
| CPU | 0.5 vCPU | 2 vCPU |
| RAM | 512 MB | 4 GB |
| Storage | 1 GB volume | 5 GB or more per node |
| Runtime | Linux container | Linux container |
Sizing is per NATS node. JetStream limits are derived at boot from the container's memory and volume, so resizing a service retunes it: a quarter of memory backs memory streams, 80% of the volume backs file streams. The UI and gateway are small.
Self-Hosting NATS
The quickest local test is one container with JetStream on:
docker run -p 4222:4222 -p 8222:8222 -v nats-data:/data \
nats:2.14-alpine --js --sd /data --user app --pass changeme
Then install the nats CLI. This creates a stream, publishes and reads back:
nats context save local --server nats://app:changeme@localhost:4222 --select
nats stream add ORDERS --subjects 'orders.>' --storage file --replicas 1 --defaults
nats pub orders.created '{"id":1,"item":"widget"}'
nats consumer add ORDERS worker --pull --deliver all --ack explicit --defaults
nats consumer next ORDERS worker --count 1 --ack
For a cluster, each node needs a unique server_name, a cluster block on port 6222 and a routes list of its peers.
Is NATS Free to Self-Host?
NATS Server is open source under Apache 2.0, with no paid tier, seat limits or enterprise-only features, and NUI is open source too. Synadia sells a managed cloud service and support, but nothing here depends on either — your only cost is the Railway resources the five services use.
FAQ
What is NATS? NATS is an open-source messaging system for service-to-service communication. It provides publish/subscribe, request/reply and queue groups over subjects, and JetStream adds durable streams plus key-value and object stores on the same connection.
What does this Railway template deploy? Five services: three NATS servers forming one JetStream cluster, each with its own volume; the NUI management interface; and a Caddy gateway putting HTTP basic auth in front of it. Passwords for the app account, system account, cluster routes and UI are generated at deploy.
Why does the template run three NATS nodes instead of one?
JetStream replicates streams using Raft, which needs a majority to agree on every write. Three nodes let you create streams with --replicas 3, so data survives a node restarting — and you cannot raise a replica count onto nodes that do not exist.
How do I connect my application to self-hosted NATS?
From another Railway service in the same project, use the private hostnames on port 4222 with the app username and NATS_PASSWORD — no public traffic, no egress cost. From outside, each node has a TCP proxy address on its service page; browsers use the wss:// form of its domain.
Do I need a separate database for JetStream? No. JetStream stores everything on the volume attached to each node, which is why all three have one. The KV and object stores use the same engine, so a small app can run on NATS alone.
Is NATS a replacement for Kafka or RabbitMQ? It overlaps with both: JetStream gives Kafka-style replayable streams and RabbitMQ-style work queues from one binary, at sub-millisecond in-memory latency. Kafka still wins on sustained throughput with long retention; RabbitMQ has richer routing.
Template Content
nats-3
gridalpha/nats-railwaygateway
gridalpha/nats-railwaynats-1
gridalpha/nats-railwaynats-2
gridalpha/nats-railway