# Setting up a Private Routing Environment

> This document is a guide for setting up The Things Network's routing services in a local or private environment. If you already have some knowledge about how th

- Type: Global update from The Things Network
- Author: Hylke Visser
- Published: 2016-12-14
- Updated: 2026-10-06
- Web page: https://www.thethingsnetwork.org/article/setting-up-a-private-routing-environment

This document is a guide for setting up The Things Network's routing services in a local or private environment. If you already have some knowledge about how the backend works and if you are comfortable with a command line, this is the perfect place to start.

**NOTE:** This guide applies to the previous version (v2) of our network stack. To get started with the current version of our network stack (v3), go to [ttn.fyi/v3/getting-started](https://ttn.fyi/v3/getting-started).

In this guide we will get everything up and running on your local machine (on `localhost`). We will rely on the community account server of The Things Network (`account.thethingsnetwork.org`) so that you can use your TTN Account to manage devices in your local network. If you don't want your environment to use community accounts, you can implement your own account server that is compliant with the [Account Server API Specification](https://www.thethingsnetwork.org/docs/draft/account/#api-reference).

## Preparation

- Install and start [Redis](http://redis.io/download).
- Install and start [Mosquitto](https://mosquitto.org/download/)
- Create a working directory. In this document we will use `~/ttn`. All commands are executed from this directory.
- Download `ttn` (`master` branch) [macOS](https://ttnreleases.blob.core.windows.net/release/master/ttn-darwin-amd64.zip), [64 bit Linux](https://ttnreleases.blob.core.windows.net/release/master/ttn-linux-amd64.zip), [32 bit Linux](https://ttnreleases.blob.core.windows.net/release/master/ttn-linux-386.zip) or [arm Linux](https://ttnreleases.blob.core.windows.net/release/master/ttn-linux-arm.zip).
- Download `gateway-connector-bridge` (`master` branch) for [macOS](https://ttnreleases.blob.core.windows.net/gateway-connector-bridge/master/gateway-connector-bridge-darwin-amd64.zip), [64 bit Linux](https://ttnreleases.blob.core.windows.net/gateway-connector-bridge/master/gateway-connector-bridge-linux-amd64.zip), [32 bit Linux](https://ttnreleases.blob.core.windows.net/gateway-connector-bridge/master/gateway-connector-bridge-linux-386.zip) or [arm Linux](https://ttnreleases.blob.core.windows.net/gateway-connector-bridge/master/gateway-connector-bridge-linux-arm.zip). We did not test this guide on Windows, but that might work too :)

## The Discovery Server

The configuration for the Discovery server will be stored in `~/ttn/discovery/ttn.yml`:

```yaml
id: mynetwork-discovery
tls: true
key-dir: discovery/
auth-servers:
  ttn-account-v2: "https://account.thethingsnetwork.org"
  local: "file://discovery/server.pub"

discovery:
  master-auth-servers:
  - ttn-account-v2
  - local
```

This configuration creates a discovery server called "mynetwork-discovery". The server will use TLS and get its keys from `./discovery`. It uses the public TTN account server and a local certificate for authentication, and both "account servers" are seen as "master servers".

With this configuration in place, we will generate the public/private keypair for the Discovery server:

```sh
$ ttn discovery gen-keypair --config ./discovery/ttn.yml
```

Since we use TLS, we need to create a certificate for `localhost`:

```sh
$ ttn discovery gen-cert localhost --config ./discovery/ttn.yml
```

You can now start the discovery server:

```sh
$ ttn discovery --config ./discovery/ttn.yml
```

Keep the discovery server running while you set up the other components.

### Access Tokens

Routers, Brokers and Handlers authenticate with the discovery server using "access tokens". In the public community network you could get those with the command `ttnctl components token [router/broker/handler] [id]`, but as we're building a private network, we have to manually create these access tokens with the command `ttn discovery authorize [router/broker/handler] [id]`. For the Router you would use the following command:

```sh
ttn discovery authorize router mynetwork-router --config ./discovery/ttn.yml
```

You can use the access token that is printed to your console in the router's configuration. Use the appropriate commands to do the same for the Broker and Handler.

## The Router

The configuration for the Router will be stored in `~/ttn/router/ttn.yml`:

```yaml
id: mynetwork-router
tls: true
key-dir: router/
auth-servers:
  ttn-account-v2: "https://account.thethingsnetwork.org"

discovery-address: "localhost:1900"
auth-token: THE DISCOVERY ACCESS TOKEN THAT YOU GENERATED FOR THE **ROUTER**

router:
  server-address-announce: localhost
  skip-verify-gateway-token: true
```

This configuration creates a router called "mynetwork-router". The server will use TLS and get its keys from `./router`. It uses the public TTN account server for authentication. It connects to the discovery server on `localhost` and uses the access token we just generated to authenticate with the discovery server. The Router announces itself as being on the `localhost` address. Furthermore it skips verification of the gateway token, because we're in a private network. You can remove this line if you do want to verify gateway tokens.

Just like with the discovery server, we have to generate a public/private keypair and a TLS certificate:

```sh
ttn router gen-keypair --config ./router/ttn.yml
ttn router gen-cert --config ./router/ttn.yml
```

In this case we don't have to specify that we're running on `localhost` when generating the certificate. The command will just take that from the configuration file.

The TLS certificate we generated for the discovery server is not trusted by the operating system, so we have to manually trust it. To do so, we can append it to a `ca.cert` file in the `router` directory:

```sh
cat discovery/server.cert > router/ca.cert
```

You will have to do the same when you set up the Broker and Handler.

You can now test your configuration by starting the Router:

```sh
$ ttn router --config ./router/ttn.yml
```

## The NetworkServer

The configuration for the NetworkServer will be stored in `~/ttn/networkserver/ttn.yml`:

```yaml
id: mynetwork-networkserver
tls: true
key-dir: networkserver/
auth-servers:
  ttn-account-v2: "https://account.thethingsnetwork.org"
```

Again we have to generate a public/private keypair and a TLS certificate:

```sh
ttn networkserver gen-keypair --config ./networkserver/ttn.yml
ttn networkserver gen-cert --config ./networkserver/ttn.yml
```

### Access Token

Brokers authenticate with the NetworkServer using access tokens. You can create an access token with the following command:

```sh
ttn networkserver authorize mynetwork-broker --config ./networkserver/ttn.yml
```

## The Broker

The configuration for the Broker will be stored in `~/ttn/broker/ttn.yml`:

```yaml
id: mynetwork-broker
tls: true
key-dir: broker/
auth-servers:
  ttn-account-v2: "https://account.thethingsnetwork.org"

discovery-address: "localhost:1900"
auth-token: THE DISCOVERY ACCESS TOKEN THAT YOU GENERATED FOR THE **BROKER**

broker:
  server-address-announce: localhost
  networkserver-cert: broker/networkserver.cert
  networkserver-token: THE NETWORKSERVER ACCESS TOKEN THAT YOU GENERATED
```

The TLS certificate we generated for the discovery server and the NetworkServer are not trusted by the operating system, so we have to manually trust them. For the discovery server, we'll do the same as in the Router, for the NetworkServer, we can append the certificate to the `ca.cert` file or we can copy the NetworkServer's certificate and set the `networkserver-cert` in the configuration to `broker/networkserver.cert`. We will do the latter.

```sh
cat discovery/server.cert > broker/ca.cert
cp networkserver/server.cert broker/networkserver.cert
```

Again we have to generate a public/private keypair and a TLS certificate:

```sh
ttn broker gen-keypair --config ./broker/ttn.yml
ttn broker gen-cert --config ./broker/ttn.yml
```

Now we will tell the discovery server which `DevAddr` prefix we will handle with this Broker. In this case, we use a prefix that TTN reserved for private networks: `26000000/20`. This prefix allows you to issue 4096 distinct addresses to devices in your private network, which should be more than enough (remember that device addresses are not unique; you can easily give 100 devices the exact same address). If you need a larger address space, you should file a request with The Things Network Foundation. If you are setting up a large network, you might have to apply for your own NetID from the LoRa Alliance, which will give you your own prefix.

```sh
ttn broker register-prefix 26000000/20 --config ./broker/ttn.yml
```

## The Handler

The configuration for the Handler will be stored in `~/ttn/handler/ttn.yml`:

```yaml
id: mynetwork-handler
tls: true
key-dir: handler/
auth-servers:
  ttn-account-v2: "https://account.thethingsnetwork.org"

discovery-address: "localhost:1900"
auth-token: THE DISCOVERY ACCESS TOKEN THAT YOU GENERATED FOR THE **HANDLER**

handler:
  server-address-announce: localhost
  broker-id: mynetwork-broker
  mqtt-address: localhost:1883
```

Again we have to copy the discovery server certificate and generate a public/private keypair and a TLS certificate:

```sh
cat discovery/server.cert > handler/ca.cert
ttn handler gen-keypair --config ./handler/ttn.yml
ttn handler gen-cert --config ./handler/ttn.yml
```

## The Bridge

TTN's backend components communicate using gRPC. Gateways, use different protocols, which are translated to gRPC with so-called bridges. The widely used `packet_forwarder` protocol is handled by the [`gateway-connector-bridge`](https://github.com/TheThingsNetwork/gateway-connector-bridge). If you followed this guide from the start, you should already have the correct version.

Unfortunately, this bridge does not support configuration files, so we will use CLI flags to configure it. We still need to have the discovery server certificate, so let's create an empty directory for the bridge and copy the discovery server certificate to the `ca.cert` file:

```sh
cat discovery/server.cert > bridge/ca.cert
```

We will need the following command line arguments to connect the bridge to your private backend:

```sh
--ttn-discovery-server "localhost:1900"
--root-ca-file "bridge/ca.cert"
--ttn-router "mynetwork-router"
```

## Starting Everything Together

Just run each of the following commands in separate tabs:

```sh
ttn discovery --config discovery/ttn.yml
ttn router --config router/ttn.yml
ttn networkserver --config networkserver/ttn.yml
ttn broker --config broker/ttn.yml
ttn handler --config handler/ttn.yml
gateway-connector-bridge  --root-ca-file "bridge/ca.cert"  --ttn-router "localhost:1900/mynetwork-router"
```

You can also use a `Procfile` as shown below and use (for example) `foreman` or `forego` to start everything.

```yaml
dsc: ttn discovery --config discovery/ttn.yml
rtr: ttn router --config router/ttn.yml
ns: ttn networkserver --config networkserver/ttn.yml
brk: ttn broker --config broker/ttn.yml
hdl: ttn handler --config handler/ttn.yml
br: gateway-connector-bridge --root-ca-file "bridge/ca.cert"  --ttn-router "localhost:1900/mynetwork-router"
```

## ttnctl

Configuration of `ttnctl` is done by editing `$XDG_CONFIG_HOME/config.yml` or `~/.ttnctl.yml`:

```yaml
discovery-address: localhost:1900
mqtt-address: localhost:1883
router-id: mynetwork-router
handler-id: mynetwork-handler
```

`ttnctl` also needs to have the discovery server certificate, for that we have to create a `ca.cert` file in `$XDG_DATA_HOME/ttnctl`, `$XDG_CACHE_HOME/ttnctl` or `~/.ttnctl/`

```sh
touch ~/.ttnctl/ca.cert
cat discovery/server.cert >> ~/.ttnctl/ca.cert
```

Now you can register your application to your private Handler:

```sh
ttnctl applications register
```

And manage your devices in the same way as you would do on the public community network.

## Questions or Issues

If you have questions or remarks after following this guide, feel free use the [forum](https://www.thethingsnetwork.org/forum/t/setting-up-a-private-routing-environment/4445) or the `#private-backend` channel on Slack. This is a community-supported guide, so please help each other out.

## What's Next

You should now have a fully operational private backend. Now you should probably do some of the following:

- [Read how to deploy the private backend using docker-compose](https://www.thethingsnetwork.org/article/deploying-a-private-routing-environment-with-docker-compose)
- Periodically update `ttn` to the latest version
- Enable persistence in Redis
- Configure backups
- Secure your MQTT/AMQP broker with usernames and passwords
- Help with development of TTN's open source routing services
