---
title: "Deploy ChirpStack | (Just Updated) LoRaWAN Network Server, REST API and MQTT Ready"
description: "ChirpStack LoRaWAN network server with REST API, MQTT broker and Postgres"
category: "Other"
url: https://railway.com/deploy/chirpstack-or-just-updated-lorawan-netwo
---

# Deploy ChirpStack | (Just Updated) LoRaWAN Network Server, REST API and MQTT Ready

ChirpStack LoRaWAN network server with REST API, MQTT broker and Postgres

**[Deploy ChirpStack | (Just Updated) LoRaWAN Network Server, REST API and MQTT Ready on Railway](https://railway.com/template/chirpstack-or-just-updated-lorawan-netwo)**

Machine-readable deploy manifest (JSON, validated by TemplateCI): https://railway.com/deploy/chirpstack-or-just-updated-lorawan-netwo/manifest.json

- **Creator:** SuperSlowSloth
- **Category:** Other

## Template content

### mosquitto

- **Image:** eclipse-mosquitto:2.1.2-alpine
- **Start command:** `/bin/sh -c 'mkdir -p /mosquitto/config /mosquitto/data && chown -R mosquitto:mosquitto /mosquitto/data && mosquitto_passwd -b -c /mosquitto/config/passwd chirpstack "$MQTT_PASSWORD" && chown mosquitto:mosquitto /mosquitto/config/passwd && chmod 0600 /mosquitto/config/passwd && printf "listener 1883\nallow_anonymous false\npassword_file /mosquitto/config/passwd\npersistence true\npersistence_location /mosquitto/data/\n" > /mosquitto/config/mosquitto.conf && exec mosquitto -c /mosquitto/config/mosquitto.conf'`

### redis

- **Image:** redis:7.4-alpine
- **Start command:** `/bin/sh -c 'chown -R redis:redis /data && exec docker-entrypoint.sh redis-server --requirepass "$REDIS_PASSWORD" --appendonly yes --dir /data'`

### rest-api

- **Image:** chirpstack/chirpstack-rest-api:4
- **Start command:** `/bin/sh -c 'exec /usr/bin/chirpstack-rest-api -server "$CHIRPSTACK_SERVER" -bind "[::]:${PORT:-8080}" -insecure'`
- **Public domain:** Yes

### postgres

- **Image:** postgres:17.10-alpine
- **Start command:** `/bin/sh -c 'mkdir -p /docker-entrypoint-initdb.d && printf "create extension if not exists pg_trgm;\ncreate extension if not exists hstore;\n" > /docker-entrypoint-initdb.d/10-chirpstack.sql; exec docker-entrypoint.sh postgres'`

### chirpstack

- **Image:** chirpstack/chirpstack:4.20.0
- **Start command:** `/bin/sh -c 'set -e
# ChirpStack on Railway: config is written at boot from the service variables, so the
# deploy form can be submitted empty. This script contains no single quote (wrapped in sh -c).
D=/tmp/cs
mkdir -p $D
cat > $D/chirpstack.toml <<EOT
[logging]
  level="info"
[postgresql]
  dsn="postgres://postgres:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:5432/postgres?sslmode=disable"
  max_open_connections=10
  min_idle_connections=0
[redis]
  servers=["redis://:${REDIS_PASSWORD}@${REDIS_HOST}:6379/"]
  tls_enabled=false
  cluster=false
[network]
  net_id="000000"
  enabled_regions=["eu868", "us915_0", "as923", "au915_0"]
[api]
  bind="[::]:8080"
  secret="${API_SECRET}"
[integration]
  enabled=["mqtt"]
  [integration.mqtt]
    server="tcp://${MQTT_HOST}:1883/"
    username="chirpstack"
    password="${MQTT_PASSWORD}"
    json=true
EOT
cat > $D/regions.toml <<EOT
[[regions]]
  id="eu868"
  description="EU868"
  common_name="EU868"
  [regions.gateway]
    force_gws_private=false
    [regions.gateway.backend]
      enabled="mqtt"
      [regions.gateway.backend.mqtt]
        topic_prefix="eu868"
        server="tcp://${MQTT_HOST}:1883"
        username="chirpstack"
        password="${MQTT_PASSWORD}"
        qos=0
        clean_session=false
        client_id=""
        keep_alive_interval="30s"
        ca_cert=""
        tls_cert=""
        tls_key=""
    [[regions.gateway.channels]]
      frequency=868100000
      bandwidth=125000
      modulation="LORA"
      spreading_factors=[7, 8, 9, 10, 11, 12]
    [[regions.gateway.channels]]
      frequency=868300000
      bandwidth=125000
      modulation="LORA"
      spreading_factors=[7, 8, 9, 10, 11, 12]
    [[regions.gateway.channels]]
      frequency=868500000
      bandwidth=125000
      modulation="LORA"
      spreading_factors=[7, 8, 9, 10, 11, 12]
    [[regions.gateway.channels]]
      frequency=867100000
      bandwidth=125000
      modulation="LORA"
      spreading_factors=[7, 8, 9, 10, 11, 12]
    [[regions.gateway.channels]]
      frequency=867300000
      bandwidth=125000
      modulation="LORA"
      spreading_factors=[7, 8, 9, 10, 11, 12]
    [[regions.gateway.channels]]
      frequency=867500000
      bandwidth=125000
      modulation="LORA"
      spreading_factors=[7, 8, 9, 10, 11, 12]
    [[regions.gateway.channels]]
      frequency=867700000
      bandwidth=125000
      modulation="LORA"
      spreading_factors=[7, 8, 9, 10, 11, 12]
    [[regions.gateway.channels]]
      frequency=867900000
      bandwidth=125000
      modulation="LORA"
      spreading_factors=[7, 8, 9, 10, 11, 12]
    [[regions.gateway.channels]]
      frequency=868300000
      bandwidth=250000
      modulation="LORA"
      spreading_factors=[7]
    [[regions.gateway.channels]]
      frequency=868800000
      bandwidth=125000
      modulation="FSK"
      datarate=50000
  [regions.network]
    installation_margin=10
    rx_window=0
    rx1_delay=1
    rx1_dr_offset=0
    rx2_dr=0
    rx2_frequency=869525000
    rx2_prefer_on_rx1_dr_lt=0
    rx2_prefer_on_link_budget=false
    downlink_tx_power=-1
    adr_disabled=false
    min_dr=0
    max_dr=5
    [regions.network.rejoin_request]
      enabled=false
      max_count_n=0
      max_time_n=0
    [regions.network.class_b]
      ping_slot_dr=3
      ping_slot_frequency=0
    [[regions.network.extra_channels]]
    frequency=867100000
    min_dr=0
    max_dr=5
    [[regions.network.extra_channels]]
    frequency=867300000
    min_dr=0
    max_dr=5
    [[regions.network.extra_channels]]
    frequency=867500000
    min_dr=0
    max_dr=5
    [[regions.network.extra_channels]]
    frequency=867700000
    min_dr=0
    max_dr=5
    [[regions.network.extra_channels]]
    frequency=867900000
    min_dr=0
    max_dr=5
[[regions]]
  id="us915_0"
  description="US915 (channels 0-7 + 64)"
  common_name="US915"
  [regions.gateway]
    force_gws_private=false
    [regions.gateway.backend]
      enabled="mqtt"
      [regions.gateway.backend.mqtt]
        topic_prefix="us915_0"
        server="tcp://${MQTT_HOST}:1883"
        username="chirpstack"
        password="${MQTT_PASSWORD}"
        qos=0
        clean_session=false
        client_id=""
        keep_alive_interval="30s"
        ca_cert=""
        tls_cert=""
        tls_key=""
    [[regions.gateway.channels]]
      frequency=902300000
      bandwidth=125000
      modulation="LORA"
      spreading_factors=[7, 8, 9, 10]
    [[regions.gateway.channels]]
      frequency=902500000
      bandwidth=125000
      modulation="LORA"
      spreading_factors=[7, 8, 9, 10]
    [[regions.gateway.channels]]
      frequency=902700000
      bandwidth=125000
      modulation="LORA"
      spreading_factors=[7, 8, 9, 10]
    [[regions.gateway.channels]]
      frequency=902900000
      bandwidth=125000
      modulation="LORA"
      spreading_factors=[7, 8, 9, 10]
    [[regions.gateway.channels]]
      frequency=903100000
      bandwidth=125000
      modulation="LORA"
      spreading_factors=[7, 8, 9, 10]
    [[regions.gateway.channels]]
      frequency=903300000
      bandwidth=125000
      modulation="LORA"
      spreading_factors=[7, 8, 9, 10]
    [[regions.gateway.channels]]
      frequency=903500000
      bandwidth=125000
      modulation="LORA"
      spreading_factors=[7, 8, 9, 10]
    [[regions.gateway.channels]]
      frequency=903700000
      bandwidth=125000
      modulation="LORA"
      spreading_factors=[7, 8, 9, 10]
    [[regions.gateway.channels]]
      frequency=903000000
      bandwidth=500000
      modulation="LORA"
      spreading_factors=[8]
  [regions.network]
    installation_margin=10
    rx_window=0
    rx1_delay=1
    rx1_dr_offset=0
    rx2_dr=8
    rx2_frequency=923300000
    rx2_prefer_on_rx1_dr_lt=0
    rx2_prefer_on_link_budget=false
    downlink_tx_power=-1
    adr_disabled=false
    min_dr=0
    max_dr=3
    enabled_uplink_channels=[0, 1, 2, 3, 4, 5, 6, 7, 64]
    [regions.network.rejoin_request]
      enabled=false
      max_count_n=0
      max_time_n=0
    [regions.network.class_b]
      ping_slot_dr=8
      ping_slot_frequency=0
[[regions]]
  id="as923"
  description="AS923"
  common_name="AS923"
  [regions.gateway]
    force_gws_private=false
    [regions.gateway.backend]
      enabled="mqtt"
      [regions.gateway.backend.mqtt]
        topic_prefix="as923"
        server="tcp://${MQTT_HOST}:1883"
        username="chirpstack"
        password="${MQTT_PASSWORD}"
        qos=0
        clean_session=false
        client_id=""
        keep_alive_interval="30s"
        ca_cert=""
        tls_cert=""
        tls_key=""
    [[regions.gateway.channels]]
      frequency=923200000
      bandwidth=125000
      modulation="LORA"
      spreading_factors=[7, 8, 9, 10, 11, 12]
    [[regions.gateway.channels]]
      frequency=923400000
      bandwidth=125000
      modulation="LORA"
      spreading_factors=[7, 8, 9, 10, 11, 12]
  [regions.network]
    installation_margin=10
    rx_window=0
    rx1_delay=1
    rx1_dr_offset=0
    rx2_dr=2
    rx2_frequency=923200000
    rx2_prefer_on_rx1_dr_lt=0
    rx2_prefer_on_link_budget=false
    downlink_tx_power=-1
    adr_disabled=false
    min_dr=0
    max_dr=5
    [regions.network.rejoin_request]
      enabled=false
      max_count_n=0
      max_time_n=0
    [regions.network.class_b]
      ping_slot_dr=3
      ping_slot_frequency=0
[[regions]]
  id="au915_0"
  description="AU915 (channels 0-7 + 64)"
  common_name="AU915"
  [regions.gateway]
    force_gws_private=false
    [regions.gateway.backend]
      enabled="mqtt"
      [regions.gateway.backend.mqtt]
        topic_prefix="au915_0"
        server="tcp://${MQTT_HOST}:1883"
        username="chirpstack"
        password="${MQTT_PASSWORD}"
        qos=0
        clean_session=false
        client_id=""
        keep_alive_interval="30s"
        ca_cert=""
        tls_cert=""
        tls_key=""
    [[regions.gateway.channels]]
      frequency=915200000
      bandwidth=125000
      modulation="LORA"
      spreading_factors=[7, 8, 9, 10, 11, 12]
    [[regions.gateway.channels]]
      frequency=915400000
      bandwidth=125000
      modulation="LORA"
      spreading_factors=[7, 8, 9, 10, 11, 12]
    [[regions.gateway.channels]]
      frequency=915600000
      bandwidth=125000
      modulation="LORA"
      spreading_factors=[7, 8, 9, 10, 11, 12]
    [[regions.gateway.channels]]
      frequency=915800000
      bandwidth=125000
      modulation="LORA"
      spreading_factors=[7, 8, 9, 10, 11, 12]
    [[regions.gateway.channels]]
      frequency=916000000
      bandwidth=125000
      modulation="LORA"
      spreading_factors=[7, 8, 9, 10, 11, 12]
    [[regions.gateway.channels]]
      frequency=916200000
      bandwidth=125000
      modulation="LORA"
      spreading_factors=[7, 8, 9, 10, 11, 12]
    [[regions.gateway.channels]]
      frequency=916400000
      bandwidth=125000
      modulation="LORA"
      spreading_factors=[7, 8, 9, 10, 11, 12]
    [[regions.gateway.channels]]
      frequency=916600000
      bandwidth=125000
      modulation="LORA"
      spreading_factors=[7, 8, 9, 10, 11, 12]
    [[regions.gateway.channels]]
      frequency=915900000
      bandwidth=500000
      modulation="LORA"
      spreading_factors=[8]
  [regions.network]
    installation_margin=10
    rx_window=0
    rx1_delay=1
    rx1_dr_offset=0
    rx2_dr=8
    rx2_frequency=923300000
    rx2_prefer_on_rx1_dr_lt=0
    rx2_prefer_on_link_budget=false
    downlink_tx_power=-1
    adr_disabled=false
    min_dr=0
    max_dr=5
    enabled_uplink_channels=[0, 1, 2, 3, 4, 5, 6, 7, 64]
    [regions.network.rejoin_request]
      enabled=false
      max_count_n=0
      max_time_n=0
    [regions.network.class_b]
      ping_slot_dr=8
      ping_slot_frequency=0
EOT
printf %s "$ADMIN_PASSWORD" > $D/admin.pw

# The migration creates the admin user; reset its password from ADMIN_PASSWORD once it exists.
(
  until /usr/bin/chirpstack -c $D set-password --email admin --password-file $D/admin.pw >/dev/null 2>&1; do sleep 3; done
  echo "admin password set from ADMIN_PASSWORD"
) &

exec /usr/bin/chirpstack -c $D
'`
- **Health check:** /
- **Public domain:** Yes

## Documentation

# Deploy and Host ChirpStack on Railway

**Built for an LLM agent to deploy and use.** There is no setup wizard and nothing to fill in: the admin password, the API secret and every database and broker password are generated at deploy time, the `admin` user exists from the first boot, and the contract below lists every URL, variable name and one script that proves it works.

ChirpStack is an open-source LoRaWAN network server: it manages gateways, devices, applications and device profiles, handles joins and uplinks and schedules downlinks, and hands device data to your application over MQTT or an HTTP/gRPC API. This template runs the full stack, a self-hosted alternative to The Things Stack and other hosted LoRaWAN network servers.

## About Hosting ChirpStack

This template runs five services: `chirpstack` (the official `chirpstack/chirpstack` image, version 4.20.0), `rest-api` (the official `chirpstack/chirpstack-rest-api` image, a JSON/REST front for the ChirpStack gRPC API), `postgres` (Postgres 17 with the `pg_trgm` and `hstore` extensions ChirpStack needs), `redis` and `mosquitto` (an MQTT broker with password authentication and a public TCP proxy). Postgres, Redis and Mosquitto each sit on a volume. The ChirpStack configuration is written at boot from the service variables, with the regions EU868, US915 (sub-band 0), AS923 and AU915 (sub-band 0) enabled, each on its own MQTT topic prefix (`eu868`, `us915_0`, `as923`, `au915_0`).

The ChirpStack web UI is on the `chirpstack` service domain. The first sign-in is user `admin` with `ADMIN_PASSWORD`; the password is reapplied on every boot. Gateways connect to the broker over MQTT (the public TCP proxy of the `mosquitto` service): run the ChirpStack Gateway Bridge on the gateway, or use a Basic Station or Semtech packet-forwarder setup that speaks MQTT, with user `chirpstack` and password `MQTT_PASSWORD`. The UDP packet-forwarder protocol on port 1700 is not available on Railway, because Railway has no UDP ingress.

### Agent contract

- Public URL (UI and gRPC / gRPC-web API): `https://` plus the `RAILWAY_PUBLIC_DOMAIN` of the `chirpstack` service, used below as `CS_URL`
- Public URL (REST API): `https://` plus the `RAILWAY_PUBLIC_DOMAIN` of the `rest-api` service, used below as `REST_URL`. Swagger UI is at `/`.
- Private URL: `http://chirpstack.railway.internal:8080` (gRPC and UI), `http://rest-api.railway.internal:8080` (REST), `mosquitto.railway.internal:1883` (MQTT)
- Public MQTT: `MOSQUITTO_PUBLIC_HOST:MOSQUITTO_PUBLIC_PORT` (the TCP proxy of the `mosquitto` service)
- Credentials: `ADMIN_PASSWORD` (user `admin`) on the `chirpstack` service; `MQTT_PASSWORD` on the `mosquitto` service (user `chirpstack`)
- Auth: sign in once to get a JWT, then send it to the REST API as the header `Grpc-Metadata-Authorization: Bearer $JWT`. The script below signs in with a gRPC-web call (`POST $CS_URL/api.InternalService/Login`), which needs only `ADMIN_PASSWORD`. MQTT clients use username `chirpstack` and `MQTT_PASSWORD`; anonymous MQTT is refused.
- Health: `GET $CS_URL/` returns 200 with no credentials

```bash
# run with CS_URL, REST_URL, ADMIN_PASSWORD, MQTT_HOST, MQTT_PORT and MQTT_PASSWORD set
EMAIL=admin
BODY=$(printf '\n%b%s\022%b%s' "\\$(printf %03o ${#EMAIL})" "$EMAIL" "\\$(printf %03o ${#ADMIN_PASSWORD})" "$ADMIN_PASSWORD")
JWT=$( (printf '\000\000\000\000%b' "\\$(printf %03o ${#BODY})"; printf %s "$BODY") | curl -s -X POST "$CS_URL/api.InternalService/Login" -H 'content-type: application/grpc-web+proto' -H 'x-grpc-web: 1' --data-binary @- | tr -c '[:print:]' ' ' | grep -o 'eyJ[A-Za-z0-9_.-]*' | head -1)
echo "jwt ${#JWT} chars"
curl -s -o /dev/null -w 'ui %{http_code}\n' "$CS_URL/"
curl -s -o /dev/null -w 'rest-no-token %{http_code}\n' "$REST_URL/api/tenants?limit=5"
curl -s -G "$REST_URL/api/tenants" --data-urlencode limit=5 -H "Grpc-Metadata-Authorization: Bearer $JWT" | grep -o '"name":"[^"]*"'
mosquitto_pub -h "$MQTT_HOST" -p "$MQTT_PORT" -u chirpstack -P "$MQTT_PASSWORD" -t test/agent -m ok; echo "mqtt-exit $?"
```

A fresh deploy prints a JWT length above zero, `ui 200`, `rest-no-token 401`, `"name":"ChirpStack"` and `mqtt-exit 0`. From there the REST API lists and creates gateways, applications, device profiles and devices (`/api/gateways`, `/api/applications`, `/api/devices`), and application data arrives on the MQTT topics `application/APPLICATION_ID/device/DEV_EUI/event/up`.

## Why Deploy ChirpStack 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 ChirpStack 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.

- **Agent-ready** - generated secrets, a contract block and one working script; no wizard before the first API call.
- **Full stack in one deploy** - network server, REST API, Postgres, Redis and an authenticated MQTT broker, wired together over the private network.
- **Redeploy-safe** - every database and the broker sit on volumes; passwords and the API secret are stable across redeploys.
- **Pinned image** - ChirpStack is pinned to 4.20.0 in the template, so a redeploy never changes the server version underneath you.

## Common Use Cases

- **Private LoRaWAN network** - run your own network server for sensors, meters and trackers without a hosted LoRaWAN subscription.
- **IoT data pipelines** - subscribe an application, database or AI agent to device uplinks over MQTT.
- **Fleet management by API** - register gateways and devices from a script through the REST API.

## Dependencies for ChirpStack Hosting

- Postgres 17 with `pg_trgm` and `hstore`, Redis 7 and an MQTT broker (Mosquitto 2), all included in this template.
- One or more LoRaWAN gateways that can reach the public MQTT proxy of the `mosquitto` service.

### Deployment Dependencies

- [chirpstack/chirpstack](https://github.com/chirpstack/chirpstack) - upstream network server
- [ChirpStack documentation](https://www.chirpstack.io/docs/) - gateways, device profiles, integrations and the API
- [chirpstack/chirpstack-rest-api](https://github.com/chirpstack/chirpstack-rest-api) - the REST API front


## Similar templates

- [Rocky Linux](https://railway.com/deploy/rocky-linux) — Hosted Rocky Linux 9 workspace with SSH and persistent storage. 🚀
- [Foundry Virtual Tabletop](https://railway.com/deploy/X5tR6G) — A Self-Hosted & Modern Roleplaying Platform
- [Letta Code Remote](https://railway.com/deploy/letta-code-remote) — Run a Letta Code agent 24/7. No inbound ports, just deploy.

Open this page in a browser: https://railway.com/deploy/chirpstack-or-just-updated-lorawan-netwo
