Self-hosted service
chirpstack/chirpstack avatar
chirpstack/chirpstack

ChirpStack runs LoRaWAN networks from Rust with a gRPC surface

ChirpStack open-source LoRaWAN Network Server

1,123 stars293 forksRustMIT

At a glance

What is it?
ChirpStack is an open source LoRaWAN network server written in Rust, with a web interface for gateways, devices and tenants, a gRPC API for integrations, and a workspace that splits the server, its backend, the API and the LoRaWAN reference components. The build story is heavy but reproducible, with Nix, Docker and cross compilation to ARM targets.
Who is it for?
ChirpStack suits a team deploying LoRaWAN hardware that needs an open network server with a multi tenant web interface, generated API clients and a documented build, and it suits operators who can run Postgres, Redis and an MQTT broker alongside it.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 2 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What running a LoRaWAN network actually involves

LoRaWAN is a low power radio protocol, and the part that is easy to underestimate is that a radio link is not a network. A device sends an uplink, a gateway receives it and forwards it, and something has to decide what that frame means: which tenant owns the device, which application the payload belongs to, whether the frame passed the integrity checks, and where the decoded payload should be delivered. That something is the network server.

ChirpStack is described as an open source LoRaWAN network server that can be used to set up LoRaWAN networks, and it is the component that turns radio traffic into addressed application messages. The user of the project is therefore an operator: someone running gateways, onboarding devices, and routing the resulting data into whatever systems consume it.

Two capabilities define the product. The first is a web interface for the management of gateways, devices and tenants. Tenants are the key concept: a LoRaWAN deployment usually serves several applications, and the tenant is the boundary that separates one application's devices and data from another's, so the administrative surface is not a convenience but the mechanism for multi tenancy. The second is data integration with the major cloud providers, databases and services commonly used for handling device data, which is how the uplink payload leaves the LoRaWAN world and reaches a customer's own stack.

The third capability is the extension point. ChirpStack provides a gRPC based API that can be used to integrate or extend the server, and given the list of generated clients in the build tooling, that API is treated as a first class published interface rather than an internal detail.

Six workspace members split server, backend and protocol code

The Cargo manifest describes the architecture more precisely than any prose could, because the workspace members are the components:

toml
[workspace]
  resolver = "2"
  members = [
    "chirpstack",
    "chirpstack-integration",
    "lrwn",
    "lrwn-filters",
    "backend",
    "api/rust",
  ]

The chirpstack member is the server itself. The backend member is the storage layer, and the dependency list shows how it is implemented: diesel and diesel_migrations for the query builder and migration framework, diesel-async with deadpool and an async connection wrapper, and tokio-postgres. The api/rust member is the generated Rust client for the gRPC surface.

The lrwn and lrwn-filters members are the least obvious and probably the most interesting. Their names point to the LoRaWAN reference implementation and a set of filters for it, and they are separate workspace members with their own make test targets, which means the protocol handling is testable and replaceable independently of the server. A research or compliance team that needs to check how ChirpStack interprets a frame has a component to point at.

The chirpstack-integration member is separate again, and it is only exercised by the test-all target, which runs the integration tests in addition to the normal suite. That is the standard structure for a system with external brokers and databases: the fast tests run without the whole environment, and the ones that need the world assembled are opt in.

The workspace metadata pins the package version to the release, the edition to 2024, the licence to MIT, and the author to Orne Brocaar. The dependency set also tells you the security posture of the project: rustls instead of the platform TLS stack, jsonwebtoken, oauth2 and openidconnect for authentication, and the p256 and rcgen crates for the elliptic curve work that key management involves.

Nix shell for development, Docker for everything else

Building ChirpStack requires Nix, which is described as recommended, and Docker. The division of labour is specific. Nix is used for setting up the development environment, which is used both for local development and for creating the binaries, while Docker is used by cross-rs for cross compiling and by some of the make commands.

That gives you two ways into the same environment. With Nix installed, you start the development shell directly:

bash
nix-shell

Without Nix, the project provides a Docker Compose based Nix environment, started with:

bash
make docker-devshell

The limitation of the Docker route is stated rather than left for you to discover. You can run the test commands and you can run cargo build inside it, but cross compiling will not work there, because it would try to start Docker within Docker.

The user interface is built separately from the Rust code, with its own target:

bash
make build-ui

So the build is not a single cargo invocation. It is a Rust workspace, a generated gRPC web layer, and a front end, wired together by make targets in a Makefile that also contains the release machinery. The front end lives in a ui/ directory with its own package.json, and the Makefile's version target rewrites the version string across the ui package file, the gRPC web package, the JavaScript, Python, Java, Kotlin and PHP API packages, and the Cargo manifest, which is the clearest evidence that the API is published for several languages as separate SDKs.

The test suite needs Postgres, Redis, Mosquitto, RabbitMQ and Kafka

Running the tests is the part of this project with the largest environmental footprint, and the documentation is upfront about it. ChirpStack requires several services, among them PostgresQL, Redis and Mosquitto, to be running before you can run the tests, and if you started the development shell with nix-shell you have to start them yourself:

bash
docker compose up -d

The compose file in the repository root shows the full set the project expects: postgres:18-alpine, redis:8-alpine, eclipse-mosquitto:2, rabbitmq:4-management-alpine and apache/kafka:4.1.0, with the ChirpStack service itself built from Dockerfile-devel and depending on all of them. The environment section points the test run at these services through variables such as TEST_POSTGRESQL_DSN, TEST_REDIS_URL, TEST_MOSQUITTO_SERVER, TEST_KAFKA_BROKER and TEST_AMQP_URL, and exposes the server on port 8080.

The test command itself is:

bash
make test

And there is a second database backend you can switch to with an environment variable, which the Makefile's test target passes through to the crates:

bash
DATABASE=sqlite make test

The difference between make test and make test-all is worth noticing, because it tells you what the project considers a supported check. The normal target runs the Rust API build, the backend cargo tests, and the per crate test suites for chirpstack, lrwn and lrwn-filters. The test-all target adds the chirpstack-integration tests and the tests that are normally ignored. In other words, the default test run deliberately avoids the components that need the whole environment assembled, and the full run is a separate, heavier target.

Migrations are make targets, and the backend is a build time choice

Schema changes are handled with three make targets. You create a new migration with a name:

bash
make migration-generate NAME=test-migration

You apply migrations with make migration-run, and you revert one with make migration-revert. The naming of the first target is the one to note, since the name argument is the thing you have to remember when you come back to it six months later.

The backend selection is consistent everywhere. By default these commands use the PostgresQL database backend, and the SQLite backend is selected by setting the DATABASE environment variable to sqlite, the same variable that the test target uses. The documentation repeats this rule for the build as well: by default the binaries are built with the PostgresQL backend, and setting DATABASE=sqlite compiles ChirpStack with the SQLite backend instead.

That makes the backend a compile time decision rather than a runtime one, which has consequences for how you ship. A binary built for SQLite is not a configuration you can switch later, and a deployment that expects to move between backends has to rebuild rather than reconfigure. For an operator this is a decision to make at build time, and for a developer it explains why the test matrix is expressed as an environment variable rather than a command line flag.

The underlying storage layer is built on diesel and diesel-async with deadpool, so the asynchronous behaviour of the connection pool is part of the design rather than an adapter, and the migration tooling is diesel_migrations.

Release binaries are built for musl targets including armv7

Building the distributable binaries takes one extra step, because packaging and cross compiling need tools that are not part of the development shell. The make target installs them:

bash
make dev-dependencies

The Makefile shows that this installs cargo-generate-rpm at version 0.19.0, which is the packaging tool for the RPM artefacts.

The build commands are then run from the chirpstack sub-folder, and the Makefile names them for what they produce:

bash
make debug-amd64
make release-amd64
make dist

The debug build is described as optimized for build speed, the release build as optimized for performance and binary size, and the dist target builds all packages, all targets, with .deb, .rpm and .tar.gz files. The dist target at the top level also runs the gRPC web generation in the api directory first, so the API artefacts are rebuilt with the binaries.

The Dockerfile is where the cross compilation target becomes concrete. The binary stage copies three release artefacts into the image, an x86_64 musl build, an armv7 musleabihf build and an aarch64 musl build, and a case statement picks the right one from the target platform. The final stage is alpine:3.24.2 with ca-certificates added, the binary copied in, the process running as nobody:nogroup with an entrypoint of /usr/bin/chirpstack.

Two design choices are worth drawing out. The static musl builds are what let one image run on a Raspberry Pi or an ARM gateway without a libc dependency, and the non-root user means the container does not need to start as root to do its work. The images are built from binaries you cross compiled yourself, not from a multi stage build that compiles Rust inside Docker, which is why cross-rs and the Docker requirement exist in the development flow at all.

MIT licence, active master, and a test tagged release

ChirpStack Network Server is distributed under the MIT licence, with the licence file at the repository root. MIT is permissive, with a notice requirement and no copyleft obligation on adopters, and for a network server that will sit between physical devices and customer systems that choice keeps the licensing conversation short.

The project is not archived, and the last push was on 2026-10-19. The recent release tags are v4.19.1 on 2026-08-04, v4.19.2 on 2026-09-19 and v4.20.0-test.3 on 2026-10-12, and the workspace version field in the Cargo manifest matches that last tag, 4.20.0-test.3. So the current source tree is a test release of the next minor version, which is a common and useful pattern for a project with a large installed base: the tag exists so people can pin it, and the test suffix exists so nobody mistakes it for a stable line.

The version scheme in the Makefile adds a detail that matters for anyone integrating against the API. A single VERSION value is pushed into the Cargo manifest, the ui package, the gRPC web package, and the JavaScript, Python, Java, Kotlin and PHP API packages, and the release is tagged twice, once as v$(VERSION) and once as api/go/v$(VERSION). The API therefore has its own version stream, and it moves with the server but is tagged separately, so a consumer of the generated clients can pin an API version independently.

The repository also carries a SECURITY.md, a rust-toolchain.toml that pins the compiler, a shell.nix for the development environment, dprint.json and taplo.toml for formatting, and an examples/ directory with plugins for ADR handling and for the event, frame, meta and request logs. The example log plugins are a good sign for an operator, because they show how to extend the server's own observability rather than only its API.

Editorial conclusion

ChirpStack suits a team deploying LoRaWAN hardware that needs an open network server with a multi tenant web interface, generated API clients and a documented build, and it suits operators who can run Postgres, Redis and an MQTT broker alongside it. It is the wrong choice if you want a single binary with no external dependencies, because the server expects a database, a cache and a message broker to be present, and a poor match if you do not want to build from source, since the documentation points to pre-compiled binaries on the project website rather than shipping one in the repository. Before adopting, pick a database backend with the DATABASE environment variable, run make test against a database you can afford to fill, and read the chirpstack.io documentation for the configuration of the tenant and gateway model.

Frequently asked questions

How do I build ChirpStack from source?

Install Nix, which is recommended, and Docker, then start the development shell with nix-shell, or with make docker-devshell if you have no Nix. Build the user interface with make build-ui, and build the release binaries from the chirpstack sub-folder with make release-amd64 or make dist.

What services do I need before running the ChirpStack tests?

ChirpStack requires services such as PostgresQL, Redis and Mosquitto to be running, and the compose file in the repository root also brings up RabbitMQ and Kafka. Start them with docker compose up -d and then run make test, or DATABASE=sqlite make test for the SQLite backend.

How are ChirpStack database migrations created and applied?

Use make migration-generate NAME=test-migration to create one, make migration-run to apply migrations and make migration-revert to revert. These commands use the PostgresQL backend by default, and the SQLite backend is selected by setting the DATABASE environment variable to sqlite.

How can I extend or integrate with ChirpStack?

ChirpStack provides a gRPC based API for integration and extension, and the build tooling generates client packages for Go, JavaScript, Python, Java, Kotlin and PHP, each versioned and tagged separately from the server.

Official sources

  1. chirpstack/chirpstack on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/chirpstack-chirpstack.svg)](https://hysenlabs.com/projects/chirpstack-chirpstack)

Was this useful?