# Deploying to production

> Run Railbase on a VPS behind a reverse proxy, with TLS and systemd.

_Updated: 2026-06-04_

Railbase runs comfortably on a small VPS: one process, one data file, no database
to operate. This guide covers a typical production setup — a reverse proxy for
TLS, a systemd unit for supervision, and the production-mode essentials.

## Production essentials

Two things separate a dev run from a production run:

```bash
export RAILBASE_PROD=true
export RAILBASE_VAULT_PASSWORD_FILE=/run/secrets/railbase-vault
```

- **`RAILBASE_PROD=true`** turns off development conveniences.
- **A real vault password** is mandatory — supplied via
  `RAILBASE_VAULT_PASSWORD_FILE` (preferred) or `RAILBASE_VAULT_PASSWORD`. In
  production, starting without one is a hard error; Railbase refuses to fall back
  to the dev key. See [Security](security).

Keep data on local disk:

```bash
./railbase serve --addr :8095 --data-dir /var/lib/railbase
```

## Reverse proxy + TLS

Terminate TLS at a reverse proxy and forward to Railbase on `:8095`. Don't expose
`:8095` to the internet directly.

**Caddy** (automatic HTTPS):

```text
app.example.com {
    reverse_proxy 127.0.0.1:8095
}
```

**nginx**:

```nginx
server {
    listen 443 ssl;
    server_name app.example.com;

    # ssl_certificate / ssl_certificate_key ...

    location / {
        proxy_pass http://127.0.0.1:8095;
        proxy_set_header Host              $host;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_http_version 1.1;
        proxy_set_header Upgrade    $http_upgrade;   # WebSocket / realtime
        proxy_set_header Connection "upgrade";
    }
}
```

> [!IMPORTANT]
> When you run behind a proxy, set `RAILBASE_TRUSTED_PROXIES` to your proxy's CIDR
> so client IPs (from `X-Forwarded-For`) are trusted correctly. Without it,
> rate-limiting and audit logs see the proxy's IP, not the client's.

## systemd unit

```ini
[Unit]
Description=Railbase
After=network-online.target
Wants=network-online.target

[Service]
ExecStart=/usr/local/bin/railbase serve --addr :8095 --data-dir /var/lib/railbase
EnvironmentFile=/etc/railbase/railbase.env
Restart=on-failure
RestartSec=2
DynamicUser=yes
StateDirectory=railbase
# data-dir above can point at /var/lib/railbase (StateDirectory)

[Install]
WantedBy=multi-user.target
```

Put your environment in `/etc/railbase/railbase.env` (mode `0600`):

```ini
RAILBASE_PROD=true
RAILBASE_VAULT_PASSWORD_FILE=/run/secrets/railbase-vault
RAILBASE_PLUGIN_MANAGER=1
RAILBASE_TRUSTED_PROXIES=127.0.0.1/32
RAILBASE_LOG_FORMAT=json
```

Then `systemctl enable --now railbase`.

## After it's up

- Create your admin: `railbase admin create you@example.com` (see [Quickstart](quickstart)).
- Set up a backup schedule — [Backups & restore](backups-and-restore).
- Review [Security](security) before exposing the admin and marketplace surfaces.

> [!TIP]
> Health endpoints `GET /healthz` and `GET /readyz` are handy for your load
> balancer or uptime checks.
