ChirpStack LoRaWAN Network 4.20.0
Overview
This guide covers launching and using the ChirpStack LoRaWAN Network 4.20.0 on Ubuntu 24.04 AMI from SecureAnyCloud on Amazon EC2. It is one version of the SecureAnyCloud Hardened EHRbase & GIS, Health and Research Platforms AMIs listing on AWS Marketplace. Pick it as the software version when you subscribe and launch.
ChirpStack is the open source LoRaWAN Network Server (MIT license). It manages your LoRa gateways, devices (OTAA and ABP), device profiles and applications. It handles joins, uplinks and downlinks, and delivers device events to your applications over MQTT, HTTP and other integrations. The ChirpStack Gateway Bridge converts your gateways' packet-forwarder traffic into MQTT for ChirpStack.
The image is the network server, not the radio hardware. You also need LoRa gateways pointed at this instance (Step 9).
What you get:
- No default admin and no demo data. ChirpStack's database migrations create the user
adminwith the published passwordadmin. On this image, your instance's first boot runs the migrations itself and givesadmina random 24-character password before any port opens;admin/adminis refused. No applications, devices, gateways or API keys ship. - Secrets of your instance's own. The admin password, ChirpStack's API signing secret (
api.secret, which signs every console token and API key), the three MQTT account passwords, the Valkey password and the TLS key are generated on your instance. A token or API key minted on another ChirpStack instance is refused here. - HTTPS from the first boot. nginx serves the console, gRPC-web and the native gRPC API on port 443 with a certificate generated on your instance; port 80 redirects to HTTPS and a request for an unknown host name gets no answer. ChirpStack itself listens only on 127.0.0.1:8080. Sign in is rate limited per address.
- An MQTT broker with no anonymous access. Mosquitto 2.1 (from the Eclipse Mosquitto maintainers' package archive) listens on 127.0.0.1:1883 only, refuses anonymous clients, and has three accounts with passwords of your instance's own and their own topic rights:
chirpstack(the network server),gateway-bridge(gateway topics only) andintegrationfor your application (it reads device events and queues downlinks, and cannot read or write gateway traffic). - A private, password-protected device-session store. Device sessions live in Valkey 7.2 (the open source Redis successor) in memory only, with no network listener: a unix socket that only Valkey and ChirpStack can open. Valkey's default (passwordless) user is off; ChirpStack signs in as its own account, which may not run admin, scripting or other dangerous commands.
- A database with no network listener and no password. PostgreSQL 16 listens on a unix socket only and ChirpStack signs in as its own operating-system user (peer authentication); no database password exists to leak. The image ships no database: your first boot creates it.
- Integrations that cannot reach inside. ChirpStack's HTTP and other integrations send to URLs a tenant user types in. By default ChirpStack may connect only to loopback and public IPv4 endpoints: private, link-local and carrier-grade NAT IPv4 ranges, your VPC's own IPv4 CIDR and all IPv6 are denied, so an integration cannot reach the instance metadata service or hosts on your private networks (Step 11 shows how to allow one endpoint in your VPC, or IPv6).
- Hardened defaults. ChirpStack, the bridge, Mosquitto, nginx, Valkey, PostgreSQL and the first boot service are blocked from the instance metadata service, and the instance requires IMDSv2. Configuration is owned by root and read only to the services. The ChirpStack package repository key is pinned by its fingerprint.
- Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.
What is on the image: ChirpStack 4.20.0, ChirpStack Gateway Bridge 4.1.2, Mosquitto 2.1.2, PostgreSQL 16, Valkey 7.2.13, nginx, Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent.
- Default user:
ubuntu - Architecture: x86_64
- Recommended instance type: m5.large
Prerequisites
- An AWS account subscribed to the listing, an EC2 key pair and a VPC subnet with a route to the internet (or a private route you use for management).
- A security group that allows TCP 22 (SSH), TCP 443 (console and API), TCP 80 (redirects to HTTPS) and UDP 1700 (your gateways) from the networks that should reach them. Keep 22 to your own networks, 443 to the networks your staff and applications use, and UDP 1700 to your gateways' addresses (see Step 9 for why).
Launch the instance
In the AWS Marketplace listing, choose Continue to Subscribe, then Continue to Configuration. Pick ChirpStack LoRaWAN Network 4.20.0 on Ubuntu 24.04 as the software version and select your Region. Then choose Launch through EC2.
Choose m5.large, your key pair and a security group as above.
Alternatively, launch with the AWS CLI. Replace the values in angle brackets:
aws ec2 run-instances --region <region> --image-id <ami-id-from-the-listing> \
--instance-type m5.large --key-name <your-key-pair> \
--security-group-ids <your-security-group> --subnet-id <your-subnet> \
--metadata-options HttpTokens=required,HttpEndpoint=enabled \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=chirpstack}]'First boot takes about a minute after the instance is running. It creates the database and runs ChirpStack's migrations.
Connect to your instance
Version SSH user
ChirpStack LoRaWAN Network 4.20.0 on Ubuntu 24.04 ubuntu
Connect over SSH with your key pair:
ssh -i <your-key>.pem ubuntu@<public-ip-or-dns>Check the services
PostgreSQL, Valkey, Mosquitto, ChirpStack, the Gateway Bridge and nginx should all be active:
systemctl is-active postgresql@16-main valkey-server mosquitto chirpstack chirpstack-gateway-bridge nginxactive active active active active activeOnly SSH, HTTP and HTTPS listen on the network over TCP. ChirpStack and Mosquitto listen on 127.0.0.1, and PostgreSQL and Valkey have no TCP listener at all (each uses a local socket):
sudo ss -tlnH | awk '{print $4}' | sort -u0.0.0.0:22 0.0.0.0:443 0.0.0.0:80 127.0.0.1:1883 127.0.0.1:8080 127.0.0.53%lo:53 127.0.0.54:53 [::]:22 [::]:443 [::]:80The Gateway Bridge takes your gateways' packets on UDP 1700:
sudo ss -ulnpH | grep ':1700 'UNCONN 0 0 *:1700 *:* users:(("chirpstack-gate",pid=19436,fd=3))Get your sign-in details
The credentials file holds the address ChirpStack is served as (your instance's public DNS name by default), the admin user and its password, and the MQTT account for your application. All are generated on this instance. Read the file:
sudo cat /root/chirpstack-credentials.txt# ChirpStack on this instance (sac). Root-only (0600). Generated at first boot: 2026-11-07T23:09:37Z CHIRPSTACK_URL=https://ec2-54-146-215-79.compute-1.amazonaws.com/ CHIRPSTACK_ADMIN_USER=admin CHIRPSTACK_ADMIN_PASSWORD=<CHIRPSTACK_ADMIN_PASSWORD>
# MQTT broker (loopback only: 127.0.0.1:1883). For your application on this instance (reads application/#,
# writes application/+/device/+/command/down): MQTT_INTEGRATION_USER=integration MQTT_INTEGRATION_PASSWORD=<MQTT_INTEGRATION_PASSWORD>
# The chirpstack and gateway-bridge MQTT accounts, the Valkey account and api.secret are in /etc/chirpstack/*.toml,
# /etc/chirpstack-gateway-bridge/chirpstack-gateway-bridge.toml and /etc/chirpstack-sac (root / service only).
# Gateways: Semtech UDP packet forwarder to <this host>:1700, region eu868 (sudo chirpstack-sac-set-gateway-region).
# Change a password:
sudo chirpstack-sac-set-password [email] Change the hostname:
sudo chirpstack-sac-set-hostname <name>Sign in to the console
Open CHIRPSTACK_URL in a browser. The certificate is self signed, so the browser warns once. Continue to the site (Step 12 replaces the certificate with your own). Visiting the instance by its IP address redirects to the configured name.
Sign in as admin with the password from the credentials file. ChirpStack opens on the dashboard of its one tenant, ChirpStack. The menu on the left has the network-server pages (tenants, users, API keys, device profiles, regions) and the tenant's pages (gateways, device profiles, applications).
A new instance has the tenant and the admin user only. Set up your network in this order:
- Tenant > Gateways > Add gateway. Enter the gateway's EUI (its Gateway ID) and a name. The gateway shows Never seen until it sends its first packet (Step 9).
- Tenant > Device profiles > Add device profile. Set the region (
EU868is enabled for the gateway bridge by default, and Step 9 changes it), the LoRaWAN MAC version and regional-parameters revision of your devices, and whether they join by OTAA. - Tenant > Applications > Add application. Then choose Add device in it. Enter the device's DevEUI and profile, then its application key (OTAA) under OTAA keys.
- Network Server > Users. Create an account for each member of staff with only the rights they need, and keep
adminfor administration.
Check HTTPS and the sign in
Port 80 redirects to HTTPS on the configured name, and the console answers on it with the instance's certificate. A request for any other host name is closed without an answer. Check all three:
curl -s -o /dev/null -w '%{http_code} %{redirect_url}\n' http://127.0.0.1/
H=$(sed -n 's/^set \$cs_host "\(.*\)";$/\1/p' /etc/nginx/chirpstack-host.conf)
curl -s --cacert /etc/nginx/ssl/chirpstack.crt --resolve "$H:443:127.0.0.1" -o /dev/null -w 'console %{http_code}\n' "https://$H/"
curl -sk -o /dev/null -w 'unknown host %{http_code}\n' -H 'Host: other.example' https://127.0.0.1/ || echo '(closed without an answer, as expected)'301 https://ec2-54-146-215-79.compute-1.amazonaws.com/ console 200 unknown host 000 (closed without an answer, as expected)The code 000 means nginx closed the connection without an answer. The certificate was generated on your instance for its name and addresses:
openssl x509 -noout -subject -ext subjectAltName -in /etc/nginx/ssl/chirpstack.crtsubject=CN = ec2-54-146-215-79.compute-1.amazonaws.com, O = sac X509v3 Subject Alternative Name: DNS:ec2-54-146-215-79.compute-1.amazonaws.com, IP Address:54.146.215.79, IP Address:172.31.85.225, IP Address:127.0.0.1, DNS:localhostThe admin password signs in over HTTPS (gRPC status 0) and reads the profile, while a call without a token, a wrong password and admin/admin are all refused (gRPC status 16, Unauthenticated):
sudo chirpstack-sac-cred-roundtripCHIRPSTACK_LOGIN_OK https 0 0 16 16 16Sign-in is limited to 10 attempts a minute per client address (after a burst of 10). Further attempts get HTTP 429 until the minute passes.
Use the API
ChirpStack's API is gRPC. On this image it is served on port 443 only. gRPC-web serves the console and browsers, and native gRPC (HTTP/2) serves API clients and the ChirpStack SDKs. Both use the instance's certificate.
Create an API key in the console. Use Network Server > API Keys for an admin key, or Tenant > API Keys for a tenant key. Copy the key when it is shown, because it is not shown again. Send it as authorization: Bearer <key>. With grpcurl from your own machine:
grpcurl -insecure -H 'authorization: Bearer <api-key>' -d '{"limit": 10}' \
<chirpstack-host>:443 api.TenantService/ListUse -cacert chirpstack.crt (the certificate from /etc/nginx/ssl/chirpstack.crt) instead of -insecure until you install a certificate your clients trust (Step 12). Without a valid key, every API method answers Unauthenticated. API keys and console tokens are signed with this instance's api.secret. Keep API keys secret, and delete the ones you no longer need.
MQTT for your application
ChirpStack publishes every device event (up, join, ack, txack, log, status, location, integration) to the local broker under application/<application id>/device/<DevEUI>/event/<type>. Your application on this instance reads them as the integration account, whose password is MQTT_INTEGRATION_PASSWORD in the credentials file.
Keep the password off the command line. Put it in a mosquitto_sub options file that only you can read:
install -d -m 0700 ~/.config
sudo sed -n 's/^MQTT_INTEGRATION_PASSWORD=/-P /p' /root/chirpstack-credentials.txt > ~/.config/mosquitto_sub chmod 0600 ~/.config/mosquitto_sub printf -- '-u integration\n' >> ~/.config/mosquitto_sub mosquitto_sub -h 127.0.0.1 -p 1883 -t 'application/#' -vEach device event arrives as one JSON line, for example after a device joins:
application/603cf866-a8a3-41dc-9c86-1a44e3eea09f/device/200b98f24d7d0807/event/join {"deduplicationId":"...","time":"...","deviceInfo":{...},"devAddr":"01b3e3be",...}The same account may queue a downlink by publishing to application/<application id>/device/<DevEUI>/command/down. It cannot subscribe to or publish on the gateway topics, and anonymous clients are refused:
mosquitto_sub -h 127.0.0.1 -p 1883 -t '#' -C 1 -W 3Connection error: Connection Refused: not authorised.The broker listens on 127.0.0.1 only. To deliver events off the instance, use one of ChirpStack's integrations (HTTP, AWS SNS, Kafka, InfluxDB and others, per application under Integrations), or run a bridge on the instance. Do not open 1883 in the security group.
Connect your gateways
Point each gateway's Semtech UDP packet forwarder (global_conf.json / local_conf.json, gateway_conf) at the instance: server_address = the instance's DNS name or Elastic IP, serv_port_up and serv_port_down = 1700. Add the gateway in the console with its Gateway ID (Step 5). It turns Online with its first status packet.
The UDP packet forwarder has no authentication or encryption. Anyone who can send UDP to port 1700 can pretend to be a gateway (inject uplinks, receive downlinks meant for that gateway ID). The image keeps the bridge because nearly every gateway speaks this protocol, but open UDP 1700 in the security group only to your gateways' public addresses, or reach the instance over a VPN. For an authenticated connection, use LoRa Basics Station instead: gateways connect over TLS with client certificates. Set the bridge's backend to Basics Station in /etc/chirpstack-gateway-bridge/chirpstack-gateway-bridge.toml ([backend] type="basic_station", with bind, tls_cert, tls_key and ca_cert under [backend.basic_station], as in ChirpStack's Gateway Bridge documentation), restart chirpstack-gateway-bridge, open its TCP port (for example 3001) to your gateways and close UDP 1700.
The bridge serves one LoRaWAN region at a time. The image starts on EU868. ChirpStack has these regions enabled:
sudo grep -A14 'enabled_regions' /etc/chirpstack/chirpstack.tomlenabled_regions = [ "as923", "as923_2", "as923_3", "as923_4", "au915_0", "cn470_10", "cn779", "eu433", "eu868", "in865", "ism2400", "kr920", "ru864", "us915_0",To serve another region, point the bridge at it. This rewrites its MQTT topic prefix and restarts it. Then choose the same region in your device profiles:
sudo chirpstack-sac-set-gateway-region us915_0chirpstack-sac-set-gateway-region: the gateway bridge now serves region us915_0A region ChirpStack has not enabled is refused, and nothing changes.
Your data
ChirpStack keeps tenants, users, gateways, devices, keys and frame metadata in the PostgreSQL database chirpstack; device sessions and the downlink queue state live in Valkey (in memory only, rebuilt from traffic). Back up the database, for example with this command:
sudo -u postgres pg_dump -Fc chirpstack > chirpstack-$(date +%F).dumpKeep backups private and encrypted, because they hold your devices' keys. Restore with pg_restore into a fresh chirpstack database owned by the chirpstack role, with ChirpStack stopped.
Integrations and private endpoints
HTTP and other integrations send from ChirpStack to the URL you enter. By default ChirpStack reaches loopback and public IPv4 endpoints only. Denied are the private IPv4 ranges (10/8, 172.16/12, 192.168/16), 100.64/10, 0/8, link-local including 169.254.169.254, all IPv6 (which includes the NAT64, 6to4 and Teredo forms that carry an IPv4 address inside an IPv6 one), and your VPC's own IPv4 CIDR blocks, which chirpstack-sac-egress-refresh reads from the instance metadata service into an nftables set for the chirpstack user at boot, before every ChirpStack start and hourly, so a VPC on a non-RFC 1918 range, or a CIDR you associate later, is covered too. That includes the instance's own private address: loopback is allowed by destination only (127.0.0.0/8 and ::1), so an integration cannot reach a local service such as SSH or nginx through it. A URL on a denied address fails with a connection error or a timeout in ChirpStack's log, so a tenant user cannot use an integration to read the instance metadata service or probe your network. The fixed ranges are systemd IPAddressDeny / IPAddressAllow settings on chirpstack.service (an allow entry wins over every deny entry); the VPC CIDRs are the nftables table inet sac_chirpstack_egress.
To send to one endpoint inside your VPC (for example an internal load balancer or a VPC endpoint), allow just that address in an override drop-in and restart ChirpStack:
sudo install -d /etc/systemd/system/chirpstack.service.d printf '[Service]\nIPAddressAllow=<private-ip-of-your-endpoint>/32\n' | sudo tee /etc/systemd/system/chirpstack.service.d/70-my-endpoint.conf
sudo systemctl daemon-reload && sudo systemctl restart chirpstackNever allow 169.254.169.254 or fd00:ec2::254 (the metadata service), and never allow a whole range such as ::/0 or 2000::/3: because an allow wins, that would also allow the metadata service and your VPC.
IPv6 integration endpoints. To let integrations reach IPv6 endpoints, replace the deny list instead of adding an allow. This drop-in resets IPAddressDeny and lists again everything SecureAnyCloud denies except ::/0, and you must add your VPC's IPv6 CIDR (VPC console, IPv6 CIDRs) to it, along with any other IPv6 prefix you route privately:
D=/etc/systemd/system/chirpstack.service.d { printf '[Service]\nIPAddressDeny=\n' grep -h '^IPAddressDeny=' "$D/50-sac-imds-deny.conf" sed -n 's#^\(IPAddressDeny=.*\) ::/0$#\1#p' "$D/60-sac-integration-egress.conf" printf 'IPAddressDeny=%s\n' '<your-vpc-ipv6-cidr>'; } | sudo tee "$D/70-allow-ipv6.conf"
sudo systemctl daemon-reload && sudo systemctl restart chirpstack systemctl show -p IPAddressDeny chirpstackThe last command should list your VPC's IPv6 CIDR and no ::/0. Delete 70-allow-ipv6.conf (then daemon-reload and restart) to go back to IPv4 only.
If the VPC lookup fails, integrations stop (fail closed). When the refresh cannot read a valid VPC CIDR list from the instance metadata service (5 tries over about a minute), it blocks all non-loopback traffic of the chirpstack user instead, so no integration can reach your VPC by accident. ChirpStack itself, the console, the API, gateways, MQTT, Valkey and PostgreSQL keep working; only integrations to outside endpoints fail. The refresh logs the failure, writes /run/sac/chirpstack-egress-locked and retries every 5 minutes. Check it and re-run it once the metadata service is reachable (IMDSv2 on, hop limit at least 1):
cat /run/sac/chirpstack-egress-locked journalctl -u chirpstack-sac-egress-refresh -n 20
sudo systemctl start chirpstack-sac-egress-refreshThe refresh also restores the table if you load your own nftables rules with flush ruleset; run it after doing so.
Your own DNS name and certificate
Point a DNS record at the instance, then set the name that ChirpStack is served as. This mints a new self signed certificate for the name, pins it in nginx (the instance's IP addresses redirect to it) and records it in the credentials file:
sudo chirpstack-sac-set-hostname <your-domain>To use a certificate from your own certificate authority instead, copy it over the generated pair and reload nginx. Running chirpstack-sac-set-hostname again replaces it with a self signed one:
sudo install -m 0644 <your-domain>.crt /etc/nginx/ssl/chirpstack.crt
sudo install -m 0600 <your-domain>.key /etc/nginx/ssl/chirpstack.key
sudo nginx -t && sudo systemctl reload nginxBehind a load balancer: terminate TLS on it and forward to port 443 on the instance with the configured host name (HTTP/2 to the target is needed for native gRPC). nginx always uses the connection's address as the client address, so every request through the load balancer counts against its sign-in budget; raise the limit in /etc/nginx/conf.d/sac-chirpstack-guard.conf if that is too tight.
Security checks
ChirpStack, the Gateway Bridge, Mosquitto, nginx, Valkey and PostgreSQL are blocked from the instance metadata service (systemd may list the addresses in a different order):
systemctl show -p IPAddressDeny chirpstack-gateway-bridge mosquitto nginx valkey-server postgresql@16-mainIPAddressDeny=::ffff:169.254.169.254/128 fd00:ec2::254/128 169.254.169.254/32 168.63.129.16/32 IPAddressDeny=::ffff:169.254.169.254/128 fd00:ec2::254/128 169.254.169.254/32 168.63.129.16/32 IPAddressDeny=::ffff:169.254.169.254/128 fd00:ec2::254/128 169.254.169.254/32 168.63.129.16/32 IPAddressDeny=::ffff:169.254.169.254/128 fd00:ec2::254/128 169.254.169.254/32 168.63.129.16/32 IPAddressDeny=::ffff:169.254.169.254/128 fd00:ec2::254/128 169.254.169.254/32 168.63.129.16/32ChirpStack itself may reach loopback and public IPv4 only (Step 11): every private, link-local and carrier-grade NAT IPv4 range and all IPv6 (::/0) are denied, your VPC's IPv4 CIDR is in the nftables set, and loopback is the only allow. systemd folds ranges inside a larger one (the IPv6 entries into ::/0, a 172.31.0.0/16 default VPC into 172.16.0.0/12) and may list them in a different order:
systemctl show -p IPAddressAllow -p IPAddressDeny chirpstack
sudo nft list set inet sac_chirpstack_egress vpc4 ls /run/sac/chirpstack-egress-locked 2>/dev/null || echo '(not locked)'IPAddressAllow=127.0.0.0/8 ::1/128 IPAddressDeny=168.63.129.16/32 169.254.0.0/16 100.64.0.0/10 172.16.0.0/12 10.0.0.0/8 0.0.0.0/8 ::/0 192.168.0.0/16 table inet sac_chirpstack_egress { set vpc4 { type ipv4_addr flags interval auto-merge elements = { 172.31.0.0/16 } } } (not locked)systemd may list the IPAddressAllow and IPAddressDeny entries in another order on each boot. The vpc4 elements are your own VPC's IPv4 CIDR blocks (172.31.0.0/16 is the default VPC).
The credentials are readable by root only, the configuration by root and the one service that uses it, and the Valkey socket by Valkey and ChirpStack only; other users cannot open it, and PostgreSQL does not answer on the network:
sudo stat -c '%a %U:%G %n' /root/chirpstack-credentials.txt /etc/chirpstack /etc/chirpstack/chirpstack.toml /etc/chirpstack-sac /etc/chirpstack-gateway-bridge/chirpstack-gateway-bridge.toml /etc/mosquitto/sac-passwd /etc/mosquitto/sac-acl /run/valkey/valkey.sock valkey-cli -s /run/valkey/valkey.sock PING || echo '(refused, as expected)' pg_isready -h 127.0.0.1 -p 5432 || echo '(no network listener, as expected)'600 root:root /root/chirpstack-credentials.txt 750 root:chirpstack /etc/chirpstack 640 root:chirpstack /etc/chirpstack/chirpstack.toml 700 root:root /etc/chirpstack-sac 640 root:gatewaybridge /etc/chirpstack-gateway-bridge/chirpstack-gateway-bridge.toml 640 root:mosquitto /etc/mosquitto/sac-passwd 640 root:mosquitto /etc/mosquitto/sac-acl 770 valkey:chirpstack /run/valkey/valkey.sock Could not connect to Valkey at /run/valkey/valkey.sock: Permission denied (refused, as expected) 127.0.0.1:5432 - no response (no network listener, as expected)The Mosquitto password file holds only salted PBKDF2 hashes of the three account passwords, and the Valkey account file only a SHA-256 hash of ChirpStack's.
Passwords
chirpstack-sac-set-password gives a ChirpStack user (by default admin) a new random password with ChirpStack's own set-password and, for admin, records it in /root/chirpstack-credentials.txt. Run it with a password on standard input to choose your own (12 to 96 letters and digits):
sudo chirpstack-sac-set-password printf '%s\n' '<new-password>' | sudo chirpstack-sac-set-password admin --stdinchirpstack-sac-set-password: the password of 'admin' was changedA password change does not end console sessions that already hold a token: tokens are signed with api.secret and expire on their own. To invalidate every token and API key at once, replace secret under [api] in /etc/chirpstack/chirpstack.toml with a new random value (openssl rand -hex 32) and restart ChirpStack; then create new API keys.
Server components
Component Path
ChirpStack settings (database, Valkey and MQTT accounts, api.secret; root:chirpstack 0640) /etc/chirpstack/chirpstack.toml, /etc/chirpstack/region_*.toml
Gateway Bridge settings (root:gatewaybridge 0640) /etc/chirpstack-gateway-bridge/chirpstack-gateway-bridge.toml
ChirpStack and bridge service hardening /etc/systemd/system/chirpstack.service.d/, /etc/systemd/system/chirpstack-gateway-bridge.service.d/
Mosquitto settings, topic rights and password hashes /etc/mosquitto/conf.d/sac-chirpstack.conf, /etc/mosquitto/sac-acl, /etc/mosquitto/sac-passwd
Valkey settings (included last from valkey.conf) and account file /etc/valkey/sac-chirpstack.conf, /etc/valkey/sac-chirpstack-users.acl
Valkey account password (root only) /etc/chirpstack-sac/
Database (PostgreSQL 16 cluster main) /var/lib/postgresql/16/main
nginx site, sign-in limit and gRPC snippets /etc/nginx/sites-available/sac-chirpstack, /etc/nginx/conf.d/sac-chirpstack-guard.conf, /etc/nginx/snippets/sac-chirpstack-*.conf
Certificate and key /etc/nginx/ssl/
First boot and helpers /usr/local/sbin/chirpstack-firstboot, chirpstack-sac-set-hostname, chirpstack-sac-set-password, chirpstack-sac-set-gateway-region, chirpstack-sac-cred-roundtrip
Image notes /usr/share/doc/chirpstack-sac/SAC.txt
Credentials /root/chirpstack-credentials.txt (mode 0600)
Updating
ChirpStack and the Gateway Bridge come from ChirpStack's apt repository (/etc/apt/sources.list.d/chirpstack.list, key pinned in /etc/apt/keyrings/chirpstack.gpg), Mosquitto from the Eclipse Mosquitto maintainers' archive (mosquitto-ppa.list, limited by /etc/apt/preferences.d/sac-mosquitto-ppa to the three Mosquitto packages), and the rest from Ubuntu. Unattended upgrades apply Ubuntu's security updates; update ChirpStack and Mosquitto yourself after a database backup (Step 10), reading ChirpStack's release notes first:
sudo apt-get update && sudo apt-get install --only-upgrade chirpstack chirpstack-gateway-bridge mosquitto mosquitto-clients libmosquitto1ChirpStack runs its database migrations when it starts. The SecureAnyCloud settings live in drop-ins under /etc/systemd/system/*.service.d/ and in /etc/chirpstack, so a package update keeps them.
Security recommendations
- Allow port 22 from your own networks only, 443 only from the networks your staff and applications use, and UDP 1700 only from your gateways (or use Basics Station, Step 9).
- Keep the admin password secret (Step 14), give each member of staff their own account with the least rights they need, and create tenant API keys rather than admin keys for applications.
- Use your own DNS name and a certificate your clients trust (Step 12).
- Back up the database regularly, encrypted (Step 10).
- Keep ChirpStack, Mosquitto and the operating system updated (Step 16).
Support and licensing
ChirpStack and the ChirpStack Gateway Bridge are free software under the MIT license (ChirpStack's license text is in /usr/share/doc/chirpstack/copyright). Mosquitto (EPL-2.0/EDL-1.0), PostgreSQL, Valkey and nginx carry their own open source licenses. There is no per-device or per-gateway license fee. This image is built and supported by SecureAnyCloud; ChirpStack is developed by Orne Brocaar and the ChirpStack contributors, and SecureAnyCloud is not affiliated with them.
- Email: sales@secureanycloud.com
Deploy on AWS
Launch ChirpStack LoRaWAN Network 4.20.0 on Ubuntu 24.04 on AWS with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com