Helium is a multi network server with a decentralized packet routing system. This is really clever and allows anyone to use the public infrastructure as private LoRaWAN compatible network. That way you get benefit of a worldwide coverage and, in the same time, the ability to protect your raw data from anyone looking at them. You can also create some other public network server, as we are doing with Helium-Iot.eu
The objective of a such public service is to offer a shorter route for your European devices and as a consequence a shorter response time for downlink. It also ensure your data to stay in Europe, something important for personal data like tracking, health or for industrial applications.
For a better understanding, let’s take a look at the Helium network architecture:

Thanks to the miner components incorporated in the hotspots, the traffic from the devices is directly routed to the right network server. Each of the network servers belongs to a, operator, it can be you or me or any established telecom operator. This is basically really cool !
That’s why we have decided with the company I work for to take a look at this business and launched Helium-IoT.eu. So you can connect your devices to Helium using our console https://console.helium-iot.eu
In the next page of this post I will explain how to become your own operator for making your private network. This is a bit complex operations, so if you want your own network server, as part of our services we are proposing to make it for you and host it. We also have solutions to migrate existing LoRaWan networks to Helium. Just let us know by contacting me with the contact link.
So, let’s be more technical to understand all of this.
Some overall information before getting started
The power of the blockchain is to allow anyone to be part of it with no specific contract other than the smart-contract. No negotiation, no long discussion with layers, no better price to obtain than any other … You just apply the common rules and get into it. In a world of big telecom companies, where all the b2b prices are secrets it makes a big difference and allows you to distribute communication over a network of more than 25.000 hotspot, worldwide, in less than a week where any technical stuff matter.
So, what we need to do to be a Helium operator is to get a OUI, basically a network operator identifier. Then we need to get some of the DevADDR to associate to the device we are going to route. After that, we have to host a router, connect it to the pair-to-pair network, run a console and start receiving IoT device data.

The Helium Network Server runs with some transactions with associated economics (always in DC, stable over time).
- OUI Creation – $100
- DevAddr Association – $100 each / minimum 8
- State_Channel – $0.35 by default every 45 blocks (about 45 minutes) = $12 / day, should be changed
- IoT Device Attachment (filter update) – $0.40 on every device addition update (can be grouped, run on every 10 minutes)
- IoT Device Communication – $0.00001 per message or 24 bytes
The communication are always paid by the router, not the device itself. In fact we can consider that a device is owned by a router, so communication are paid by that router when accepting each of the packets.
You should read State Channel documentation. Apparently, State channel is a sub chain to track the transactions related to the communication between hotspot and router. The duration of the state channel impact the frequency of Packet Transfer payment. 2 state channels are opened, the second one have a life cycle twice of the setting made for the first one. this ensure you have one state channel when renewing the previous one. When the state channel is open, on top of the cost, there is a “state channel amount” DC blocked to to ensure you will pay for the communications during the state_channel duration. At end of the state channel duration you get it back. Once this amount in consumed, the state channel is renewed. This is about 1000 DC. (by default, this means the real price of transferring 24 bytes of data is not 1DC but 18.5DC… because 2 time this amount of DC is reserved). All of this will be part of the parameters.
The DevAddr are multiplexed over different devices, there is nothing defined currently but a factor of 10 to 500 active devices per DevAddr should make sense.
In term of security, the network server own the encryption keys and is in charge of decrypting the payload. This is the way it is able to differentiate the different devices behind the same DevAddr: only one of them have the right key to get a valid frame after decryption. This is also why Helium allows to have a public network acting as a private network. Only the right network server is manipulating the decrypted payload.

As you can see the traffic stay encrypted from the device to the Network Server. The Device data is not stored in the blockchain and none of the component between device to Network Server knows the secret keys. If you own the Network Server (router+console), you are the only one to access the keys.
Let’s implement it
The following part of the blog post describes the way I’ve created my own Network Server. For doing this I had to use different wallet version, some not yet officially published. The wallet should soon been updated (if not yet) to includes the fixes and this documentation should be ok. But currently it could not work on every steps.
By following this tutorial, you are going to spend more than $900 in DC / HNT. if you make something wrong or if I mis-document something you can loose this money. So you decide or not to follow this tutorial, you decide to do it on your own and I won’t be responsible of any loss you could have.
As a reminder, we are providing on helium-iot.eu Helium Network Servers as a Service to avoid all this complexity.
Create a Wallet
A wallet is needed to execute the operation to get the OUI and DevAddr range. The binary version was not working on my centos due to library incompatibility.
Install RUST
Following the recommendations on rust-lang.org, this is processing the installation automatically.
[~] curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
Source the rust environement:
[~] source $HOME/.cargo/env
Install the needed dependencies
[~] yum install gcc openssl-devel
Build Wallet
Download the wallet sources from helium wallet Github releases ; uncompress it, then enter the directory and run compilation:
[~helium] cargo build --release
Now you can run the wallet from target/release and run a first test command
[~helium] cd target/release/
[~wallet] ./helium-wallet -h # It shows help
Create the wallet
You can create a basic wallet (1 key file) or a sharded wallet (multiple keys) (see helium github). Here is the creation of a basic wallet.
[~wallet] ./helium-wallet create basic
The creation process asks for a password… do not loose it. And it generate a wallet.key file you need to backup this file.
The creation of the wallet will terminate with a print of your wallet address:
+-----------------------------------------------------+------------+...
| Address | Balance | Data
+-----------------------------------------------------+------------+-...
| 13HWrdkMoooYJEYXXXXXXXXXXXZifCzwxPaCnV9XXXbC5Gso1ic | 0.00000000 | 0...
+-----------------------------------------------------+------------+-...
Transfer credits to the wallet
To operate on Helium, you need to create an OUI and at least 8 DEVADR. The OUI allows you to generate DEVEUI and DEVADR are the address used by the device once they have been joined the network. As explained in this page, you can multiplex the DEVADR. The OUI cost 10M DC, the DEVADDR also 10MDC each, you can by them by 8/16/32… So at start, you need to pay 90M DC to start being an operator. Depending on the HNT value, you will need to transfer to this wallet the right amount.
The DevAddr multiplexing select a device address in regard of the original location of the device to spread the device over the available DevAdr. When the number of DevAddr is too low compare to the number of running device, the network server will need more time to find the right device and there is a risk to be late on time for operations such as downlink which are time critical.
So you need to have the credits for the OUI/DEVADDR creation. From you hotspot wallet of binance wallet, you can make a transfert to the wallet created. I recommend to start by a 1 to 10 HNT transfer, then going further with the expected amount. Then you need to transfer the equivalent of 90M DC ($900).
Check your wallet balance to see the credit appearing:
[~wallet] ./helium-wallet balance
+-----------------------------------------------------+--------------+...
| Address | Balance |
+-----------------------------------------------------+--------------+...
| 13HWrdkMoooYJEYXXXXXXXXXXXZifCzwxPaCnV9XXXbC5Gso1ic | 220.00000000 |
+-----------------------------------------------------+--------------+...
Now the HNT needs to be burned in DC
[~wallet] ./helium-wallet burn --amount 2xx --payee 13HWrdkMoooYJEYXXXXXXXXXXXZifCzwxPaCnV9XXXbC5Gso1ic --commit
The transaction processing takes some time (you can check the status with the hash returned by the previous command on https://api.helium.io/v1/pending_transactions/hashValue.
Once you have 90M or more DC you can proceed to OUI creation.
Create OUI and DevADDR
Now we are going to proceed OUI + 8 DevADDR creation (this is burning the 90M DC)
[~wallet] ./helium-wallet oui create --subnet-size 8 --filter wVwCiewtCpEKAAAAAAAAAAAAcCK3fwAAAAAAAAAAAABI7IQOAHAAAAAAAAAAAAAAAQAAADBlAAAAAAAAAAAAADEAAAA2AAAAOgAAAA --commit
This is generating the OUI creation transaction indicating the Requested OUI
+------------------------+---------------------------------------------+
| Key | Value |
+------------------------+---------------------------------------------+
| Requested OUI | 5 |
+------------------------+---------------------------------------------+
| Reqeuested Subnet Size | 8 |
+------------------------+---------------------------------------------+
Now, we can process to the router / console installation, see next page…
Pre-requisites
You need to have some components like docker-ce. For installing it on centos, you can follow the official installation guide. Then you need to install docker-compose:
[~] sudo curl -L "https://github.com/docker/compose/releases/download/1.28.5/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
[~] sudo chmod +x /usr/local/bin/docker-compose
Start docker
[~] sudo systemctl start docker
[~] sudo systemctl enable docker
The system require a postgresql database, this one is included in a container during the build phase. You can modify the name and password by editing the docker-compose.yaml file. This also contains the directory where the database files will be stored on your filesystem to be persistent.
postgres:
image: postgres
container_name: helium_postgres
restart: always
environment:
- POSTGRES_DB=helium_console
- POSTGRES_PASSWORD=xxx
volumes:
- "./data/postgres/:/var/lib/postgresql/data"
Then you need a Auth0 account for managing user connection. Auth0 have a free subscription up-to 7000 users. This is currently, apparently, the only one identity provider supported by the open-source console. I recommend to create a new tenant with your domain in it and deactivate the social login until you completely setup it or you are going to have a big warning on the front page. Upload your logo. You need to setup the Application Type as Native. After registering your first user you need to deactivate application registration in the advanced settings. Take also a look at the 2 factor authentication parameters and reCaptcha options.
Install console & router
Basically, the role of the router is to receive a data block from a device. It decides to accept it or reject it. When accepting it, this will burn the associated DC for this communication. The wallet for burning DC is your wallet.

The role of the console is to associate new devices, watch data, manage integration with a third party IoT application… This is the relation between the user and its devices.
To install the console, the best is to follow the Helium documentation described in the linked page. As a precision:
- in the .env file you need to setup your database endpoint
DATABASE_DB=helium_console
DATABASE_HOST=postgres
DATABASE_USER=helium_console
DATABASE_PASSWORD=xxx
SOCKET_CHECK_ORIGIN=https://*.helium-iot.eu
- in the .env-router file, you should change the SEED_NODES with Ip of currently running Helium Miner. The usual miner port is 44158, so make sure you use the right IP and PORT. One seed node is enough to start. You may also change the State channel expiration time (in block). This is corresponding to state_channel renewal, this operation costs 35000DC ($0,35) every-time.
ROUTER_SEED_NODES=/ip4/34.222.64.221/tcp/2154,/ip4/34.208.255.251/tcp/2154
ROUTER_SC_OPEN_DC_AMOUNT=50000
ROUTER_SC_EXPIRATION_INTERVAL=5000
ROUTER_XOR_FILTER_WORKER=true
ROUTER_HTTP_CHANNEL_URL_CHECK=false
ROUTER_SC_EXPIRATION is between 15 and 10080 (1 week). I currently assume the max value is 5040, I won’t detail here, see github open issue.
ROUTER_HTTP_CHANNEL_URL_CHECK avoid the router to check the domain validity before executing the http integration. (This should be enabled but I figured out some bug with it)
- In the config/config.exs file
# Configures the endpoint
config :console, ConsoleWeb.Endpoint,
url: [scheme: "https", host: "xxxm-iot.eu", port: 443],
...
config :console,
auth0_baseurl: System.get_env("AUTH0_BASE_URL")
- In the config/release.exs file, change you host name (used for redirecting)
config :console, ConsoleWeb.Endpoint,
server: true,
url: [host: "console.helium-iot.eu", port: 80]
or in https (recommended)
config :console, ConsoleWeb.Endpoint,
server: true,
url: [scheme: "https", host: "console.helium-iot.eu", port: 443],
force_ssl: [rewrite_on: [:x_forwarded_proto]],
cache_static_manifest: "priv/static/cache_manifest.json",
- Get docker-compose.yaml from templates and copy it in console directory. Then in the file you need to setup the database according to your env file:
postgres:
image: postgres
container_name: helium_postgres
restart: always
environment:
- POSTGRES_DB=helium_console
- POSTGRES_PASSWORD=xxx
- POSTGRES_USER=helium_console
volumes:
- "/opt/console/data/postgres/:/var/lib/postgresql/data"
...
router:
...
volumes:
- "/opt/console/data/router:/var/data"
- Create directory for data
[~] mkdir /opt/console
- Other settings
When a user is creating an account in the console, it automatically get a free amount of DC. By default it is 10.000 ($0,1) free communication. If you want to change this amount, you need to modify the code as it is hardcoded in it. Take a look in file lib/console_web/controllers/organization_controller.ex
Build the containers
In the console directory tree run the docker compose script to build the conatiner
[~console] /usr/local/bin/docker-compose build
[~console] /usr/local/bin/docker-compose up
The creation takes some time … (a while) so … At end, the application is running. You can stop it with a CTRL+C.
Quick access to the log later:
[~] docker logs --follow helium_router
In case you want to rebuild all that stuff (basically destroy everything)…
[~] docker system prune -a # this is destroying all what exists in docker
[~console] rm -rf ./data/postgres # this is destroying the database
I also had some issues when changing the .env-router file content to get the router container updated. I had to:
[~] /usr/local/bin/docker-compose stop
[~] /usr/local/bin/docker-compose rm router
{~] /usr/local/bin/docker-compose up -d
Load A blockchain snaPshot
This step is not mandatory but the blockchain sync car be a bit long. So you can load a snapshot from another miner you own.
Get the snapshot from your miner:
[~] docker exec minerX miner snapshot take /var/data/snap_1
The snapshot file will be created in the data directory of your miner with name snap_1. Move the file to the router. The file is moved to the console/data/router directory. Then you can load it on the router:
[~] docker exec helium_router router snapshot load /var/data/snap_1
Once done you can check the chain height and verify it is near the one of your running miner.
[~] docker exec helium_router router info height
Backup you router swarm_key
The router use a swarm_key (like the miner) for authentication. This file is like the wallet.key for the wallet. So you need to save it in case you need to restore the router somewhere else. This file is located in:
[~console] data/router/blockchain/swarm_key
Backup it !
Start console & router
Once everything is OK, you can run it in background instead of foreground
[~console] /usr/local/bin/docker-compose up -d
# later you can stop it with
[~console] /usr/local/bin/docker-compose stop
# end restart with
[~console] /usr/local/bin/docker-compose start
You can make some basic check after 5 minutes. Like ensuring you have some peers.
[~] docker exec helium_router router peer book -s
+--------------------------------------------+-------------------+
| address | name
+--------------------------------------------+-------------------+
|/p2p/<span style="color:#a30089" class="tadv-color">11wcuSbKLHDLE3XX3FbKC7KF6bgAHUDqPzv1FTE</span>|refined-...........|
+--------------------------------------------+-------------------+
+--------------------------+
|listen_addrs (prioritized)|
+--------------------------+
|/ip4/185.3X.1XX.X/tcp/2154|
+--------------------------+
+------------------+---------------------+--------------------------
| local | remote | p2p
+------------------+---------------------+--------------------------
|/ip4/172.21.0.4/tc|/ip4/24.251.6.1XX/tcp|/p2p/1122P9dzzSDcmgBxLBNj1
|/ip4/172.21.0.4/tc|/ip4/2.70.2X.4/tcp/44|/p2p/112AKGW4qMeVcwBk2KpXC
|/ip4/172.21.0.4/tc|/ip4/73.71.1XX.142/tc|/p2p/112mEVXHvkYTpHJcE5SGQ
|/ip4/172.21.0.4/tc|/ip4/74.89.1XX.175/tc|/p2p/117DmoqANL8aMJcFKjNXz
|/ip4/172.21.0.4/tc|/ip4/185.1XX.184.16/t|/p2p/11QxjZpR4Xbzb6mpjGo1F
|/ip4/172.21.0.4/tc|/ip4/194.152.2XX.36/t|/p2p/11hDybc5G2WXPHKj5ePjw
|/ip4/172.21.0.4/tc|/ip4/173.31.1XX.201/t|/p2p/11jSj6M9y5z3JTG1J3BWe
+------------------+---------------------+--------------------------
Associate this router to the OUI created
Now, we need to associate the router to the OUI we have created. In this step we are going to use the wallet used to create the OUI and the router P2P address (in red on the previous figure. Replace the –oui X by the number of your OUI.
[~router] docker exec helium_router router peer addr
/p2p/11wcuSbKLHDLE3XX3FbKC7KF6bgAHUDqPzv1FTEJHDIOE
[~wallet] ./helium-wallet oui update routers --oui X --nonce 1 --address 11wcuSbKLHDLE3XX3FbKC7KF6bgAHUDqPzv1FTEJHDIOE --commit
The transaction execution can be monitored on explorer using the ID of the wallet registering the router: https://explorer.helium.com/accounts/13HWrdkMoooYJEYXXXXXXXXXXXZifCzwxPaCnV9XXXbC5Gso1ic
Add credit to your router
Ok, some element you need to understand: when a packet is sent to the router, it will accept of reject it. If the router accept it, it will have to pay the communication in DC. I means the DC are not payed by the device itself or the end-user. It is payed by the router (OUI owner). So the final user does not really own DC.
For this reason, we need to credit the router (not the wallet used for creating the OUI). This can be done by burning HNT from the wallet with the router as a payee. The router is creating a 2 state_channel with for each a reserve of 2 x ROUTER_SC_OPEN_DC_AMOUNT each. The maximum duration of the state channel will be ROUTER_SC_EXPIRATION_INTERVAL blocks (about minutes). So assuming you have ROUTER_SC_OPEN_DC_AMOUNT = 50000 and ROUTER_SC_EXPIRATION_INTERVAL = 5000 and you want the router to have DC for a month, you need to credit about 270k DC.
./helium-wallet burn --amount 9.9 --payee 11wcuSbKLHDLE3XX3FbKC7KF6bgAHUDqPzv1FTEJHDIOE --commit
You can now (wait a bit then) check the router in the explorer to see the DC owned and the state_channel_open transaction on https://explorer.helium.com/accounts/11wcuSbKLHDLE3XX3FbKC7KF6bgAHUDqPzv1FTEJHDIOE :

Now, your router is ready to proceed packets.
Expose the console on Internet
Now you need to access the console. Instead of directly exposing the port 4000, we are going to pass this traffic through a nginx and install a ssl certificate. Currently it’s really a challenge to have the console on a specific path so it is really recommended to have it on a subdomain like console.foo.com
Install NGINX
[~] sudo yum install epel-release
[~] sudo yum install nginx
[~] sudo systemctl enable nginx
[~] sudo systemctl start nginx
Configure NGINX
Create some directories ; don’t forget to give them the correct authorization when using selinux
[~] sudo mkdir -p /www/html
[~] sudo mkdir -p /www/logs
[~] sudo chown nginx:nginx /www/html /www/logs
[~] sudo setsebool -P httpd_can_network_connect 1
[~] sudo chcon -Rv --type=httpd_sys_rw_content_t /www/html
[~] sudo chcon -Rv --type=httpd_log_t /www/logs
Add a virtual host configuration file in /etc/nginx/conf.d/helium_console.conf
upstream helium_console {
server localhost:4000;
}
server {
gzip on;
gzip_types text/plain text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript application/json;
server_name helium-iot.eu;
root /www/html;
error_log /www/logs/error.log;
access_log /www/logs/access.log;
listen 80;
location ~ ^/socket/ {
proxy_pass http://helium_console;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location ~ {
proxy_pass http://helium_console$request_uri;
proxy_redirect off;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
}
}
Then restart nginx
[~] sudo systemctl restart nginx
Then let’s make certbot do the https configuration
[~] sudo cerbot --nginx
Now you can create your console account and then you are connected.
![]() | ![]() |
Connect your first device
You can follow the device attachment I’ve been described in my previous post “first steps with Helium IoT network” to add your device in the console.
Devices are not immediately active: a blockchain transaction is needed to make the Hotspots able to route them to the right router. This operation is scheduled on every 10 minutes at the router level when ROUTER_XOR_FILTER_WORKER=true
You can accelerate this by manually executing a transaction to add the devices:
docker exec helium_router router device xor --commit
This transaction has a cost 40.000DC ($0,40), it inserts all the pending devices. Once done, the device will be able to connect:

Upgrade console & router
It’s critical to maintain the router up-to-date, for this you can run it manually on regular basis:
[~] docker pull quay.io/team-helium/router:latest
or
[~console] /usr/local/bin/docker-compose pull
Or script it (thank you Mikael) and cron it
#!/bin/bash
docker pull quay.io/team-helium/router:latest | grep -q 'is up to date' || /usr/local/bin/docker-compose up --detach router
Update the console:
- save your configuration files
- config/release.exs
- docker-compose.yaml
- update the repository
git pull
- replace the config/release.exs and docker-compose.yaml
- rebuild
[~] /usr/local/bin/docker-compose build
You may be ready or restarting
Connect to router to get informations
You can connect to the router internal for some operations like getting the router DC balance
[~] docker exec -it helium_router _build/default/rel/router/bin/router remote_console
(router@127.0.0.1)1>
Then use the following command using your router id:
(router@127.0.0.1)1>blockchain_ledger_v1:find_dc_entry(libp2p_crypto:b58_to_bin("11wcuSbKLHDLE3XX3FbKC7KF6bgAHUDqPzv1FTEJHDIOE"), blockchain:ledger()).
{ok,{data_credits_entry_v1,183,15698887}}
You also have some docker command you can use:
# See all the account balance on your router
[~] docker exec -it helium_router _build/default/rel/router/bin/router dc_tracker info all
# see all the declared devices on your router
[~] docker exec -it helium_router _build/default/rel/router/bin/router device all
# .. see other command on github/helium/router README.md file
Thanks for a great post!
Some notes on hardware requirements that might be useful for people setting up their own server:
* The server needs at least 40GB hard disk space. The block chain database grows over time. I had allocated 32GB disk but that was not sufficient.
* The server needs at least 2GB ram. I had initially allocated 1GB but this caused the server to swap constantly.
Thank you Mikael, you right I did not described the server environnement as it is small. on top of this, I could add you may have different environments to not keep the wallet within the router.