---
title: "Deploy NATS | (Just Updated) JetStream Streams Survive Redeploys, Locked Behind A Password"
description: "NATS JetStream broker. Password on, streams kept on a volume, public URL"
category: "Storage"
url: https://railway.com/deploy/nats-or-just-updated-jetstream-streams-s
---

# Deploy NATS | (Just Updated) JetStream Streams Survive Redeploys, Locked Behind A Password

NATS JetStream broker. Password on, streams kept on a volume, public URL

**[Deploy NATS | (Just Updated) JetStream Streams Survive Redeploys, Locked Behind A Password on Railway](https://railway.com/template/nats-or-just-updated-jetstream-streams-s)**

Machine-readable deploy manifest (JSON, validated by TemplateCI): https://railway.com/deploy/nats-or-just-updated-jetstream-streams-s/manifest.json

- **Creator:** SuperSlowSloth
- **Category:** Storage

## Template content

### nats

- **Image:** nats:2.15.0-alpine@sha256:ac8f88a6494bffc2c2a5289a0ca61cb28a9145c11ba5677cf24265d07f46d8d4
- **Start command:** `/bin/sh -c 'set -e; if [ -z "$NATS_USER" ] || [ -z "$NATS_PASSWORD" ]; then echo "FATAL: NATS_USER and NATS_PASSWORD must be set before NATS will start"; exit 1; fi; M=$(cat /sys/fs/cgroup/memory.max 2>/dev/null || echo max); if [ "$M" != max ]; then MM=$((M / 100 * 50)); else MM=1073741824; fi; set -- $(df -Pk /data | tail -n 1); FS=$(($2 / 100 * 90 * 1024)); echo "[railway] jetstream max_memory_store=$MM max_file_store=$FS owner=$(stat -c %u:%g /data) writable=$(test -w /data && echo yes || echo NO)"; { echo "host: \"::\""; echo "port: 4222"; echo "http_port: 8222"; echo "server_name: \"nats-railway\""; echo "jetstream { store_dir: \"/data\", max_memory_store: $MM, max_file_store: $FS }"; echo "authorization { user: \"$NATS_USER\", password: \"$NATS_PASSWORD\" }"; } > /etc/nats-railway.conf; exec nats-server -c /etc/nats-railway.conf'`

## Documentation

# Deploy and Host NATS on Railway

NATS is a lightweight, high-performance messaging system for microservices, IoT and edge
workloads. With JetStream switched on it also stores messages: durable streams, replay,
consumers with acknowledgements, key-value buckets and object stores, all from one small binary.

This template runs NATS 2.15 as a single service from a digest-pinned official image: the client
port on a Railway TCP proxy, the monitoring port on the private network, and a Railway volume
holding every JetStream stream.

## About Hosting NATS

- **JetStream is on and its data survives redeploys.** The stock `nats` image starts with
  JetStream off and no volume, so streams, key-value buckets and consumers do not exist or vanish
  with the container. Here the store lives on the volume at `/data`; a stream with messages in it
  was read back intact after a redeploy.
- **A password is required from the first connection.** A username and password are generated per
  deploy; anonymous clients and wrong passwords get `Authorization Violation`.
- **Limits follow your plan.** The start command reads the container's memory limit and the
  volume's size and sets the JetStream memory and file store limits from them (50% of memory,
  90% of the disk), instead of letting the server size itself from the host.
- **Two ways in.** Services in the same project connect with the private URL; clients elsewhere
  use the TCP proxy URL. The monitoring endpoint (`/jsz`, `/varz`) stays on the private network.

## Common Use Cases

- A message bus between microservices: request/reply, pub/sub and queue groups
- Durable work queues and event streams with replay, using JetStream consumers
- Key-value and object storage next to your messaging, without another database
- Lightweight telemetry ingestion from devices and edge clients

## Dependencies for NATS Hosting

- One Railway volume for JetStream data (created by the template)

### Deployment Dependencies

- NATS documentation: https://docs.nats.io
- JetStream: https://docs.nats.io/nats-concepts/jetstream
- Official image: https://hub.docker.com/_/nats

### Implementation Details

| Variable | Purpose |
| --- | --- |
| `NATS_USER` | Username for connections, generated per deploy. |
| `NATS_PASSWORD` | Password for connections, generated per deploy. |
| `NATS_PRIVATE_URL` | `nats://` URL on the private network, for services in the same project. |
| `NATS_PUBLIC_URL` | `nats://` URL on the TCP proxy, for clients outside Railway. |
| `NATS_MONITORING_PRIVATE_URL` | HTTP monitoring endpoint on the private network. |

Python (`nats-py`):

```python
import asyncio, nats

async def main():
    nc = await nats.connect("")
    js = nc.jetstream()
    await js.add_stream(name="EVENTS", subjects=["events.&gt;"])
    await js.publish("events.signup", b"hello")

asyncio.run(main())
```

## Why Deploy NATS on Railway?

Railway is a singular platform to deploy your infrastructure stack. Railway will host your
infrastructure so you don't have to deal with configuration, while allowing you to vertically and
horizontally scale it.

By deploying NATS on Railway, you are one step closer to supporting a complete full-stack
application with minimal burden. Host your servers, databases, AI agents, and more on Railway.


## Similar templates

- [Garage S3 Storage](https://railway.com/deploy/garage-s3-storage) — Ultra-light S3 server: fast, open-source, plug-and-play.
- [Redis](https://railway.com/deploy/redis-1) — Self Host Latest Redis with Railway
- [EasyImg](https://railway.com/deploy/easyimg) — Simple self-hostable Nuxt.js personal image hosting system.

Open this page in a browser: https://railway.com/deploy/nats-or-just-updated-jetstream-streams-s
