The Things Stack: a LoRaWAN network server with a stated compatibility contract
The Things Stack, an Open Source LoRaWAN Network Server
At a glance
- What is it?
- The Things Stack is The Things Industries' Apache-2.0 licensed Go implementation of a LoRaWAN network server, with a network server, an application server, a join server and an OAuth 2.0 identity server, following the LoRaWAN Network Reference Model. Its distinguishing feature is not the feature list but the six release commitments, which guarantee that gateway and application APIs, storage migrations, the CLI and the configuration stay stable within a major version.
- Who is it for?
- Adopt The Things Stack if you are running a private or public LoRaWAN network and want the network server, the join server, the application server and the identity layer to come from one vendor with a written compatibility contract, because the six commitments are what let you upgrade a deployment without breaking your gateways and applications.
- Can I use it commercially?
- Yes. Apache-2.0 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 1 day ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A network server built to the reference model
The Things Stack is an open source LoRaWAN network stack suitable for large, global and geo-distributed public and private networks as well as smaller networks, and its architecture follows the LoRaWAN Network Reference Model for standards compliance and interoperability. It is actively maintained by The Things Industries.
The protocol it implements is worth a sentence for readers who do not know LoRaWAN. It is a protocol for low-power wide area networks, which allows for large scale Internet of Things deployments where low-powered devices efficiently communicate with Internet-connected applications over long range wireless connections. The devices are typically battery sensors, and the network's job is to get their few bytes up and down efficiently over a long range.
That context explains the design priorities visible in the feature list. Adaptive Data Rate is supported, because a LoRaWAN device's transmit rate is adapted to the radio conditions and the network has to keep track of it. All three device classes are supported, Class A, Class B and Class C, which differ in when the device opens its receive window. Both activation methods are supported, OTAA and ABP, and OTAA is the one that involves the join server. MAC Commands are supported, which are the in-band instructions a network sends to a device to adjust its session.
The version coverage is broad and precise. The network server supports LoRaWAN 1.0, 1.0.1, 1.0.2, 1.0.3, 1.0.4 and 1.1, and the Regional Parameters 1.0, 1.0.2 rev B, 1.0.3 rev A, 1.1 rev A and 1.1 rev B. Those regional parameter revisions matter more than they look, because the frequency plans and duty cycle rules differ by region and a deployment in one region can behave incorrectly under another region's parameters.
The primary language is Go, the licence is Apache-2.0 with the file at the root, the default branch is `v3.37`, and the last push was on 2026-10-14. The recent releases are v3.36.2 from 2026-08-17, v3.36.1 from 2026-06-22 and v3.36.0 from 2026-04-03, and the repository is not archived.
What the network server does, and the one box it leaves empty
The feature list is organised by component, and reading it component by component tells you what the stack is made of: a LoRaWAN Network Server, a LoRaWAN Application Server, a LoRaWAN Join Server, an OAuth 2.0 Identity Server, gRPC APIs, HTTP APIs, a Command-Line Interface and a Web Interface, the Console.
The identity server handles user management, entity management and ACLs, which is the layer that makes the stack multi-tenant rather than a single operator's network.
The CLI covers account creation and login, application management and traffic, end device management, status and traffic, and gateway management and status. The Console covers the same ground through a web interface.
Now the gap, and it is stated clearly in the list. Under the Network Server, the item LoRaWAN Back-end Interfaces 1.0 is unchecked. Under the Application Server, Implements LoRaWAN Back-end Interfaces 1.0 is also unchecked. The Join Server, by contrast, has Implements LoRaWAN Back-end Interfaces 1.0 checked, and also Implements LoRaWAN Back-end Interfaces 1.1 draft 3.
That is a specific and consequential asymmetry. The Back-end Interfaces specification is the layer that lets a network server and a set of application servers communicate with each other directly rather than through the network server's own interface. A deployment that expects the network server to route between application servers through the back-end interfaces will not get that behaviour from this stack, because the network server and the application server do not implement version 1.0 of them. The join server, which handles session key derivation, is the piece that does.
This is also a good example of why the feature list is worth reading line by line. Everything is checked except two items, and the two items happen to be the ones a particular architecture depends on.
Payload conversion, MQTT, webhooks and the join server
The application server is where the device data turns into application data, and three things are implemented there.
Payload conversion of well-known payload formats handles the common encodings without custom code. Payload conversion using custom JavaScript functions handles the rest, and the dependency list explains the mechanism: `github.com/dop251/goja` is a JavaScript engine in pure Go, so the conversion functions run inside the server without a separate JavaScript runtime. For a device fleet with a dozen payload variants, that is a substantial saving.
The MQTT pub/sub API lets applications subscribe to uplink events over MQTT, and the HTTP Webhooks API pushes them over HTTP to a server you control. Both are checked.
The join server implements OTAA session key derivation, supports external crypto services, and, as noted, implements the Back-end Interfaces 1.0 and the 1.1 draft 3. The external crypto services support is the piece to understand architecturally. Deriving session keys is a security boundary, and letting that derivation happen in a separate service, which can be an HSM or a dedicated key service, means the key material does not have to exist inside the network server process.
The service names in the repository reflect this composition. The stack binary is `ttn-lw-stack` and the CLI is `ttn-lw-cli`, with `stack` and `cli` as convenience symlinks, and the module path is `go.thethings.network/lorawan-stack/v3`.
The repository also carries data directories for the radio side of the problem: `data/lorawan-frequency-plans`, the per-region frequency plan definitions, and `data/lorawan-webhook-templates`, the templates that shape webhook payloads, plus `data/lorawan-devices-index` for the device model catalogue. Those are copied into the image, which is a hint about how much of a LoRaWAN deployment is really about regional radio configuration rather than protocol code.
The six release commitments are the real contract
The Commitments and Releases section is the most valuable part of the README, and it is unusual for a project of this size to state its compatibility policy this explicitly.
The first commitment is that the API towards gateways and applications will not be broken within a major version, which includes how gateways communicate with the Gateway Server and how applications work with data through the Application Server. Your devices and your application integration survive a major version upgrade.
The second is that storage will be upgraded from older versions within the major version via migrations, so an older setup can be migrated without losing data. The third is the complement: storage migrations are not required within a minor version, so you can take patch updates without a database migration.
The fourth commitment is that the public command-line interface and configuration will not be broken within the major version, which means scripts and configuration files can be carried forward safely. The fifth is that the API between components and events will not be broken within minor versions, so at least the same minor versions of components are compatible with each other, which is what lets you run a multi-node deployment and upgrade nodes without ordering the upgrade.
And the sixth is the one that cuts the other way. The project reserves the right to fix bugs in API, configuration and storage in patches and minor updates, and says plainly that this may break components, gateways and applications that rely on buggy behaviour.
That last point deserves a moment's thought, because it is a real risk rather than a formality. A deployment that has come to depend on a bug will break when the bug is fixed, and the commitment says so in advance. The alternative reading is that this is an unusual level of honesty, and that a team that states its sharp edges is easier to plan around than one that does not.
One practical instruction follows from the same section. Because the project is continuously adding functionality and fixes, and is also introducing new configurations and new defaults, the README recommends reading the release notes before upgrading to a new version.
Bringing up a local stack with Compose
There is no single install command in the README. Installation is delegated to a getting started guide, and local development to `DEVELOPMENT.md`, and the repository ships a Compose file that is the fastest way to see the whole thing running.
The Compose file has three services. PostgreSQL 14 with the root credentials and a database named `ttn_lorawan_dev`, published on `127.0.0.1:5432`, and Redis 7 run with `--appendonly yes` on `127.0.0.1:6379`. The stack service runs the `thethingsnetwork/lorawan-stack` image with the entrypoint `ttn-lw-stack -c /config/ttn-lw-stack-docker.yml` and the command `start`, and it depends on both Redis and Postgres.
The environment is where the wiring becomes visible:
environment:
TTN_LW_BLOB_LOCAL_DIRECTORY: /srv/ttn-lorawan/public/blob
TTN_LW_REDIS_ADDRESS: redis:6379
TTN_LW_IS_DATABASE_URI: postgres://root:root@postgres:5432/ttn_lorawan_dev?sslmode=disableThe port list is a good summary of the architecture. The stack exposes 1700/udp, which is the gateway port, and pairs of ports from 1881 and 8881 through 1885 and 8885 plus 1887 and 8887, which are the HTTP and gRPC interfaces for the various services, with the non-TLS ports commented out for a public deployment. There are also secrets for custom certificates, `ca.pem`, `cert.pem` and `key.pem`, and an `extra_hosts` entry for `host.docker.internal`.
The container image itself is built from Alpine. A builder stage copies the `ttn-lw-stack` and `ttn-lw-cli` binaries and the frequency plan and webhook template data, and the runtime stage is `alpine:3.24` with a dedicated non-root user and group at uid and gid 886, named `thethings`, owning its own cache directory. The image exposes the same port set and defines a health check that curls `http://localhost:1885/healthz` once a minute.
That health check detail is what tells you the orchestration side of the deployment is designed for Kubernetes rather than for a single VM.
Go 1.27, forks and pinned dependencies
The `go.mod` is worth reading, because it explains a lot about how this project is maintained.
The module is `go.thethings.network/lorawan-stack/v3`, it requires Go 1.27 with the toolchain pinned to go1.27.0, and it is a v3 module path, which is consistent with the compatibility commitments: the major version is in the import path, so a breaking change is a new module path.
What is unusual is the set of `replace` directives, and each one carries a comment explaining why it exists. The mapstructure dependency is replaced with a fork from The ThingsIndustries, because of an upstream pull request. `gocloud.dev` is pinned to v0.41.0 because new versions drop support for the AWS SDK v1. The tablewriter dependency is pinned to v0.0.5 for the same reason of stability.
Two of them are about the project's own tests and features rather than about upstream churn. `github.com/redis/go-redis/v9` is pinned to v9.18.0 because new versions break Network Server tests. And the `uptrace/bun` family, which is the query builder and the PostgreSQL driver, is pinned to v1.2.15 because new versions break support for specifying a SQL migration file.
That last one is the interesting pin. The storage layer's migration mechanism is part of the product, because the second commitment says migrations will be provided within a major version, and that promise depends on being able to name a specific migration file. Pinning the driver is what keeps that promise implementable.
The rest of the dependency list shows the shape of the stack: the AWS SDK in both v1 and v2, the AWS SDK v2 config and S3 service, Bleve for search, the Paho MQTT client, an SMTP library, a JavaScript engine for the payload conversion, and a `protoc-gen-go-flags` plugin from The Things Industries, which is how the command line flags are generated from the configuration definitions.
Where The Things Stack is the wrong tool
There are three cases where this stack is the wrong answer, and the first is about who operates it.
The first is a very small deployment. The stack is a multi-tenant system with an identity server, ACLs, a join server and a distributed storage model, and the Compose file shows a Postgres, a Redis and a service that opens a dozen ports. A single sensor reading a temperature every ten minutes does not need an OAuth identity layer, an ACL system and a gRPC API surface. Something much smaller is the right instrument there.
The second is an architecture that depends on the Back-end Interfaces between the network server and the application servers, and this is the concrete gap rather than a matter of taste. The join server implements version 1.0 and the 1.1 draft 3, and the network server and application server do not implement version 1.0. If your plan is a network server fronting several application servers, or several operators sharing one network, you need to look at what the missing implementation means for you.
The third is a team that does not want to run the thing. The README is direct about the commercial option, stating that hosted solutions, as well as commercial support and consultancy, are offered by The Things Industries. For a company whose LoRaWAN deployment is a side project, a hosted offering is a smaller commitment than running Postgres, Redis and a Go service, and the open source stack is a better fit for an operator who wants control of the radio side.
The alternative approach, and the one to weigh against, is to run a different LoRaWAN network server implementation, or to use a hosted network, and to build only the pieces you need. The difference in approach is between an open stack that gives you the whole reference model with a written compatibility contract, and a hosted service that gives you the same protocol behaviour with none of the operations.
For a public network operator, the commitments in this README are the reason to choose it. They are specific, they are published, and they are the sort of thing that a two-year migration project can be planned around.
Editorial conclusion
Adopt The Things Stack if you are running a private or public LoRaWAN network and want the network server, the join server, the application server and the identity layer to come from one vendor with a written compatibility contract, because the six commitments are what let you upgrade a deployment without breaking your gateways and applications. Do not adopt it if you need the LoRaWAN Back-end Interfaces 1.0 in the network server or the application server, because both are marked as not implemented. Verify three things first: that the LoRaWAN version and regional parameters your devices use are in the supported list, that the Back-end Interfaces gap does not affect your integration plan, since the join server implements version 1.0 while the other two components do not, and that you read the release notes before each upgrade, because new configurations and new defaults are introduced with each release.
Frequently asked questions
What is The Things Stack and what does it include?
It is an open source LoRaWAN network stack in Go, actively maintained by The Things Industries, with a LoRaWAN Network Server, an Application Server, a Join Server, an OAuth 2.0 Identity Server, gRPC and HTTP APIs, a command-line interface and a web console called the Console. The architecture follows the LoRaWAN Network Reference Model.
Which LoRaWAN versions does The Things Stack support?
The network server supports LoRaWAN 1.0, 1.0.1, 1.0.2, 1.0.3, 1.0.4 and 1.1, along with Regional Parameters 1.0, 1.0.2 rev B, 1.0.3 rev A, 1.1 rev A and 1.1 rev B, and it supports Class A, B and C devices with both OTAA and ABP activation.
Does The Things Stack implement the LoRaWAN Back-end Interfaces?
Only the join server does, with version 1.0 and the 1.1 draft 3. The Network Server and the Application Server both have the Back-end Interfaces 1.0 item marked as not implemented in the feature list.
What compatibility guarantees does The Things Stack make?
Six commitments: the gateway and application APIs, storage migrations, the public CLI and configuration are stable within a major version, no storage migrations are needed within a minor version, component and event APIs are stable within minor versions, and the project reserves the right to fix bugs in API, configuration and storage in patches and minor releases.
What does it take to run The Things Stack locally?
The repository ships a docker-compose file with PostgreSQL 14, Redis 7 and the stack image, wired through the TTN_LW_REDIS_ADDRESS and TTN_LW_IS_DATABASE_URI environment variables. The README points to a getting started guide for installation and to DEVELOPMENT.md for a local development environment, and the image exposes the gateway port on 1700/udp plus paired HTTP and gRPC ports from 1881 to 8887.
Official sources
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.
[](https://hysenlabs.com/projects/thethingsnetwork-lorawan-stack)Was this useful?