Building an IoT Edge Application with and Docker (Part 1)

Following up my previous blog post about edge computing, in this blog post I will show how to build an edge computing application by combining EDGE and Remote Manager with a number of other applications. These are Docker, as everything will be deployed in containers, Portainer, for providing a nice remotely-accessible web interface for managing Docker, as well as InfluxDB, a well-known time-series database. The actual “computing” aspect of this demo will be a bit limited, as we’re mostly writing sensor data into a time-series database and then building a dashboard. However, the setup will allow to add some sensor data analytics later on. EDGE will run a small JavaScript application that periodically acquires sensor data from an industrial-grade multi-sensor device (Bosch CISS) connected via USB, and write the sensor data (temperature, humidity, acceleration) into an InfluxDB database. I will use InfluxDB’s dashboard capabilities to create a dashboard showing the data. It’s also possible to add Grafana to the setup, for even better dashboarding capabilities. Remote Manager will provide remote access to all applications, via the Remote Manager Gateway application, also running in a Docker container.

On the hardware side I will use a Raspberry Pi 4 (running Raspberry Pi OS Lite, formerly known as Raspbian) as edge device, although the whole setup can be replicated on any platform that runs Docker. 

An interesting aspect of this demo is that it shows the EDGE can be used in two ways. On the one hand, EDGE provides quite comprehensive C++ and JavaScript APIs, giving you lots of flexibility for building fully custom IoT and edge device applications. On the other hand, EDGE can also be used “out of the box” (in our case, using the available Docker image), as a “low-code” platform for writing edge applications in JavaScript, by just using the already available APIs and device integrations. In this demo I will focus on the latter. Edge Demo Setup Edge Computing Demo Setup

This blog post is split into two parts. In this first part, I will describe how to set up the basic system. In the second part, I will show how to build the JavaScript program that reads the data from the sensor and writes it to InfluxDB.


In the following I assume you have some basic understanding of Docker and Docker Compose. Some experience working with a Raspberry Pi is nice to have as well, but not required. However, it’s also possible to run the containers on any machine where Docker is available. 

Preparing the Environment

I’m starting with a clean Raspberry Pi OS Lite 64-bit image on an SD card (32 GB or more). Note that a 64-bit image is required because of InfluxDB, which currently only supports 64-bit architectures. The Raspberry Pi OS Lite 64-bit image can be downloaded from the Raspberry Pi Downloads server, using this link. Use the latest image version available, at the time of writing this was dated 2021-05-28. Open the image in Raspberry Pi Imager tool and write it to an SD-Card. Click the CHOOSE OS button, then select Use custom (the last option) and select the 64-bit image (2021-05-07-raspios-buster-arm64-lite.img). Then proceed as usual. The rest of the Raspberry Pi setup works just as with the “regular” 32-bit Raspberry Pi OS. Note that at the time of writing, the 64-bit version is still in beta, so some things may not work.

Note: it’s possible to run a variant of that setup on a 32-bit Raspberry Pi OS. For that you have to replace InfluxDB 2.0 with 1.8 and additional install Grafana for the graphing/dashboard capabilities.

First step, as usual, is expanding the file system to make all space on the SD card available, enabling SSH access and, finally, upgrading all Linux packages to their latest versions:

$ sudo apt-get update && sudo apt-get upgrade

You can verify that the Raspberry Pi is running a 64-bit OS with:

$ uname -m

Optional Step: In order to monitor the memory usage of containers with Docker (and Portainer), two additional arguments must be passed to the Linux kernel at boot time, to enable cgroup memory accounting. This can be done by editing the /boot/cmdline.txt file and adding cgroup_enable=memory cgroup_memory=1 to the end of the line.

Next is installing Docker. The easiest way is by running the convenience installer script from the Docker website. A quick word of warning: before running scripts downloaded from a website, always make sure to inspect the script first. To download and run the script, use the following commands:

$ curl -fsSL -o
$ sudo sh

This will download the script from the Docker website and execute it to install the latest Docker version. After the script completes, we can verify that Docker was successfully installed by running:

$ sudo docker version

At the time of writing this post, the current version was 20.10.7.

Note that docker must be run with sudo. For convenience, it’s possible to add the user account you’re using (pi) to the docker group to avoid having to use sudo all the time:

$ sudo usermod -aG docker pi

It’s also possible to run the hello-world container to make sure everything is okay:

$ sudo docker run hello-world

This should print out some information that everything’s properly running.

Next, I’m also going to install Docker Compose, as it will make it easier to get all containers up and running. On the Raspberry Pi, Docker Compose must be installed via Python 3’s pip package manager (pip3), which requires Python 3.x to be installed first:

$ sudo apt-get -y install libffi-dev libssl-dev python3-dev python3 python3-pip
$ sudo pip3 -v install docker-compose

Successful installation can be verified by running:

$ docker-compose -v

which will show the version number of the installed docker-compose script.

This completes the basic infrastructure setup. Before moving on, I’ll connect the CISS sensor to one of the Raspberry Pi’s USB ports and verify that it shows up in the /dev filesystem. It should show up as a new device file /dev/ttyACM0.

$ ls -l /dev/ttyACM0
crw-rw---- 1 root dialout 166, 0 Jul 9 07:17 /dev/ttyACM0

Note that accessing this device requires either root or membership in the dialout group. More on that later.

Note: Make sure that the Raspberry Pi is connected to a sufficiently powerful power adapter. A Raspberry Pi 4 with an additional device (CISS multi-sensor) connected via USB requires significant energy, which is more than standard USB-C ports can provide. If you experience sudden freezes, check the kernel log (dmesg) for undervoltage detected messages. Remote Manager

I will use Remote Manager to remotely access the Raspberry Pi (via SSH) and the applications it runs ( EDGE, Portainer, InfluxDB) once the device has been deployed to the final location (e.g., shop floor, etc.), where I no longer can directly access it in my local network. This requires setting up an account on our free Remote Manager server. After registering your new account and confirming your email address, sign in to the Remote Manager at and click the user icon or your email address on the top right. Remote Manager Account Settings

You will then see your Account page, and on it you will find your Domain ID. Copy that to the clipboard (by clicking the Copy button next to the ID) and save it in a text document. You will need it later on when setting up the Remote Manager Gateway container.

Remote Manager Account Settings

Composing the Containers

Next I’m going to set up the containers. For that purpose, I’m going to use Docker Compose, and will write a docker-compose.yml file that specifies all the containers I want in my demo.

The following container images will be used:

  • macchina/edge-ce: This is the GPL-licensed open source version of EDGE, as available from GitHub.
  • macchina/rmgateway: This is the Remote Manager Gateway server, which will make SSH access and the various web interfaces of the applications available remotely via Remote Manager.
  • portainer/portainer-ce: Portainer provides a web-based user interface for Docker. We will only use Portainer to show that its web user interface can be accessed remotely via Remote Manager. Of course, you can use Portainer to set up additional containers, as desired.
  • influxdb:2.0: InfluxDB 2 provides a time-series database and includes a nice web-based user interface that, among other features, supports building dashboards.

The docker-compose.yml file for launching all of the above containers is nothing special. It sets up the four containers according to their respective documentation and also creates volumes where all data is persistently stored. It also exposes the ports for the various applications’ web servers. The docker-compose.yml file requires an environment file, which sets the domain ID to use for connecting to the Remote Manager server. The GitHub repository also contains a .env file which defines the REMOTE_MANAGER_DOMAIN environment variable. This must be set to the Domain ID obtained earlier, when setting up the Remote Manager account.

To set-up all the containers, clone the GitHub repository, edit the .env file and set REMOTE_MANAGER_DOMAIN, then run docker-compose:

$ git clone
$ cd meta-macchina-edge-demo
$ vi .env # change REMOTE_MANAGER_DOMAIN
$ sudo docker-compose up -d

To verify that all applications work, you can open the different web applications in your browser. The web applications run on the following numbers:

The default username for EDGE is edgeadmin. The password is the same as the username. See the README for how to set a different password.

For logging in to Remote Manager Gateway, you use the username and password from your Remote Manager account created earlier.

When opening the Portainer web application for the first time, you will be asked to set a new username and password.

The default username for InfluxDB (as set in the docker-compose.yml) is macchina. The password can also be found in the docker-compose.yml file.

Setting Up Remote Access

As the final step in this first part, I will set up remote access to all applications via Remote Manager. To begin, go to the Remote Manager Gateway web application on port 8080. You will see a sign-in page, where you need to sign-in with your Remote Manager username and password, as you’ve set when registering for the account.

Remote Manager Gateway Login

Enter your credentials and click the Login button.

Next you will see an empty list of local devices. Click the plus (+) button to create the first device connection.

Remote Manager Gateway Dashboard

The first device connection will be for remote SSH access to the Raspberry Pi. Under Name, enter Raspberry Pi. ID and Domain can be left at the defaults. The ID has been randomly generated, and the Domain is the one specified in the Docker Compose environment file (REMOTE_MANAGER_DOMAIN). Then click Next to set up the IP address and SSH port.

Remote Manager Gateway - Create Device

Setting up the host IP address is a bit tricky, as there is no reliable way of determining the actual host IP address from within a Docker container on Linux. If the host has a fixed IP address, or a (local) domain name, we can use that. In my case, I will simply use raspberrypi as the host name, as my local router assigns that to the Raspberry Pi. Note that (the default in the web user interface) will not work, as is something different inside a container – it’s the container’s loopback address. 

Remote Manager Gateway Device Page

The SSH Server Port must be set to 22 (the default SSH port number in use) in order to enable remote access to SSH. The HTTP Server Port can be left as is, or set to empty – it will not be used. Clicking on Save brings you back to the Local Devices page, where you should now see the Raspberry Pi. After a few seconds, you should also see the Raspberry Pi in the Remote Manager Server web user interface, and should be able to connect to SSH via Remote Manager – more on that below.

Remote Manager Gateway

Next we will create device connections for EDGE, Portainer, InfluxDB and Remote Manager Gateway itself. Click the plus (+) button again to create another device connection. For Name, enter EDGE, leave the rest at the defaults and click Next. Set Device IP Address or Domain Name to macchina-edge (the service name defined in docker-compose.yml, which Docker’s internal DNS server maps to the container’s IP address in the Docker network set up for the Docker Compose stack, so that other containers can use it) and HTTP Server Port to 22080 and click Save.

Repeat the above for Portainer (Device IP Address or Domain Name: portainer, HTTP Server Port: 9000), InfluxDB (influxdb, 8086) and finally Remote Manager Gateway (rmgateway, 8080). When done, the Local Devices list should like like this:

Remote Manager Gateway

You should now also see all these devices in the Remote Manager Server Devices list. Clicking on the device name will bring you to the web interface of the respective application.

For SSH remote access, you can either install the Remote Manager Helper application if you are using Windows, or use the WebTunnelSSH command-line tool from the Remote Manager SDK. Remote Manager Devices Page

With Remote Manager Helper installed, you can click the Shell (>_) icon to launch the Windows SSH client (or PuTTY) via Remote Manager Helper for connecting to the Raspberry Pi. Alternatively, you can use WebTunnelSSH from the shell. Binary downloads for WebTunnelSSH for various platforms are available here. You can also build from source.

$ WebTunnelSSH -l pi

You will be asked for your Remote Manager username and password, then, like with any ssh session, for the password for the pi user on the Raspberry Pi. Note that the Device ID in the URL passed to the WebTunnelSSH command (4ef4e66d-942c-48aa-9aeb-12d00b3573d4) is the one from creating the device connection in Remote Manager Gateway. You can get that URL by right-clicking the device name in the Remote Manager server device list and selecting Copy Link (or Copy Link Address).

To connect to the other web applications ( EDGE, InfluxDB, etc.), click on the respective name on the Remote Manager Server Devices page. A new tab will then open with the login page of the respective application.

This concludes the first part of this article. In the second part, I will first show how to write a script that reads sensor data from the CISS sensor and writes the data to InfluxDB, using the InfluxDB HTTP API. Then I’ll set up a simple dashboard to visualize the data with InfluxDB.

On to part 2.