Improve this doc

AWS IoT Integration

The Amazon Web Services Internet-of-Things (AWS IoT) service enables bi-directional communication between Internet-connected things, such as sensors, embedded devices, or appliances, and other services on the AWS cloud, such as cloud servers, databases, analytics and more. This document provides an overview how to use AWS IoT component with balena to deploy IoT devices on the AWS IoT platform.

Configuring AWS IoT

Sign up for an AWS account or log into your account at the AWS Console. Once logged in, navigate to the AWS IoT console from AWS services dashboard:

AWS services dashboard

For more details about the following steps, please check the AWS Documentation, and in particular the AWS IoT Documentation. Most tasks are available both in the web interface and through the AWS Command Line Interface (CLI).

Creating Things

Physical devices that are sending and/or receiving data are called "things" on AWS IoT. They can connect to the platform using certificates, and have to have rules defined to give the device ability to communicate with AWS services. Optionally they are grouped with a thing type to make configuration of similar devices easier.

As a first experience with AWS IoT, you can try the interactive tutorial.

AWS IoT is available in selected regions, and all setup involves selecting your desired region.

Manual Thing Creation

Manual device creation involves setting up all relevant configuration either in the web console, or through the CLI. Select your thing type (optionally), and create a new thing for each of the physical devices you would like to use (that is for each of the boards you will be provisioning on balena).

AWS IoT Resources

Create and activate certificates for each of the devices, attach each to the corresponding thing, and save the "private key" and "certificate" files: those will provide authentication for the physical devices when they try to connect to AWS IoT. The private key can only be downloaded in this step, if lost it needs to be regenerated.

Finally, add some policies to let the devices communicate with the AWS platform. The simplest policy allowing all actions on all available resources (such as things, policies, MQTT channels for communication, and so on):

AWS IoT Simple Policy

After this steps, you'll have all information and setup required to connect a physical device to AWS IoT!

Automatic Thing Creation

All of AWS can be controlled over API calls, and AWS itself can be used to automate the creation of the things, certificates, policies, and other settings. Such automatic setups would tap into your balena resources, and when a new device is created, would automatically set up the required resources, and would notify the device of its credentials.

One such possible automatic setup example, balena-aws-lambda uses the AWS Lambda platform to run "serverless" AWS provisioning.

All automatic provisioning method would use the AWS IoT API.

Configuring Balena

Go to your balena dashboard and create a new application with the physical device type you are using.

The next step depends on whether you are doing manual or automatic device creation on AWS IoT.

Manual Device setup

If you are using manual device creation, then you must have set up a corresponding thing and certificate for each balena device. In this case you will likely need a number of environment variables defined, some application-wide environment variable if the setting applies for all devices (such as AWS region), and some per-device (such as authentication credentials). The environment variables will be available from the code running on your device to correctly connect to AWS IoT.

The environment variables can not contain new-line characters (they can only be a single line), while the AWS IoT private key and certificates do use information in multiple lines. Some possible tricks are base64 encoding the credentials before adding them as an environment variable and decoding it within your application software.

Automatic Device setup

Automatic device setup would involve the newly provisioned balena device notifying your AWS IoT setup service (from the earlier steps), which in turns sets up the credentials, and for example sets them up as environment variables for the device. An example of this automatic setup, working with the "balena-aws-lambda" above, is balena-aws-device. The automatic setup procedure generally depends on your device software and the service you use for AWS provisioning.


Data communication to and from AWS IoT is done over a number of protocols, the main ones being MQTT, HTTP REST API, and MQTT Over the WebSocket Protocol. See more information regarding these in the documentation. An important information is the list of connection endpoints and parameters for the specific IoT regions.

Communication with AWS IoT usually involves authenticating with the AWS service, which might require the AWS IoT Root Certificate, available from here, and also included in other certificate stores, such as certifi for Python.

Using Python

The boto3 package is a Python SDK for AWS services, and it is capable of both working with the AWS IoT resources and the data communication on the IoT Data Plane. Thus it can be used to implement both the provisioning and the device side of the application.

For easy communication between the devices and AWS IoT an MQTT library is recommended, such as paho-mqtt.

Here are a few notes using Python with balena devices. Starting from a Dockerfile templates, build on the balena default Python images, for example:

FROM balenalib/%%BALENA_MACHINE_NAME%%-python

Add the relevant dependencies to your requirements.txt file, for example


Later in your Dockerfile.template you can then install it as:

COPY requirements.txt ./
RUN pip install -r ./requirements.txt

Then in your application, you can access the environmental variables through os.getenv(VARIABLE), and send messages through the MQTT library. A very simple example is shown below:

import base64
import json
import os
import ssl

import certifi
import paho.mqtt.client as paho

# Set up AWS variables
awshost = os.getenv("AWS_HOST", "")
awsport = os.getenv("AWS_PORT", 8883)
thing_name = os.getenv("UUID")

def on_connect(client, userdata, flags, rc):
    """Send data once when connected connection
    print("Connection returned result: " + str(rc) )
    value = 42
    data = {"state": {"reported": {"reading": value}}}
    mqttc.publish("$aws/things/{}/shadow/update".format(thing_name), json.dumps(data), qos=1)
    print("msg sent: temperature " + "%.2f" % tempreading )

def set_cred(env_name, file_name):
    """Turn base64 encoded environmental variable into a certificate file
    env = os.getenv(env_name)
    with open(file_name, "wb") as output_file:

# Set up key files
key_filename = "aws_private_key.key"
set_cred("AWS_PRIVATE_KEY", key_filename)
cert_filename = "aws_certificate.crt"
set_cred("AWS_CERTIFICATE", cert_filename)

mqttc = paho.Client()
mqttc.on_connect = on_connect


mqttc.connect(awshost, awsport, keepalive=60)

It sets up key files from the environment variables, as the MQTT library used requires those credentials to be available as files. Those files are set normally set up on the volatile portion of the file system within the Docker container, thus they will not be kept upon restarting the application, making them more secure.

Using Node.js

The AWS Javascript SDK package is capable of both working with the AWS IoT resources and the data communication on the IoT Data Plane. Thus it can be used to implement both the provisioning and the device side of the application. However for security reasons it isn't encouraged to use the AWS Javascript SDK on devices in the field, it is better instead to just use the AWS IoT device SDK doesn't have resource management capabilities. Therefore for this example, we have split the code into two parts. balena-aws-lambda is responsible the resource provisioning and balena-aws-device only handles data communication.

For a complete Node.js example, please see the pair of balena-aws-lambda and balena-aws-device repositories!

Here are a few notes using the AWS IoT device SDK with balena devices. Using Dockerfile templates, start from the balena default Node.js images, for example:

FROM balenalib/%%BALENA_MACHINE_NAME%%-node:latest

Add the aws-iot-device-sdk dependency in your package.json in your application's folder:

npm install --save aws-iot-device-sdk

Later in your Dockerfile.template you can then configure the node modules installation as:

COPY package.json ./
RUN JOBS=MAX npm i --unsafe-perm --production && npm cache clean

Then in your application you can access the environmental variables through process.env.VARIABLE, and send messages through the SDK. A simple example is shown below (where new-lines in the relevant environment variables were escaped base64 encoded strings):

var awsIot = require('aws-iot-device-sdk');
var Chance = require('chance'); // used to randomise bool values
var chance = new Chance();

var device = awsIot.device({
privateKey: new Buffer(process.env.AWS_PRIVATE_KEY, 'base64'),
clientCert: new Buffer(process.env.AWS_CERT, 'base64'),
    caCert: new Buffer(process.env.AWS_ROOT_CA, 'base64'),
  clientId: process.env.BALENA_DEVICE_UUID,
    region: process.env.AWS_REGION

device.on('connect', function() {
  // publish data
  setInterval(function () {
    var reading = chance.floating({min: 0, max: 200});
        device.publish('sensor', JSON.stringify({ reading: reading }));
  }, process.env.INTERVAL || 3000);

device.on('message', function(topic, payload) {
  console.log('message', topic, payload.toString());


You can create base64 encoded strings from key files to be use with environment variables for example in Python as:

import base64

filename = "KEYFILE"
with open(filename, "rb") as key_file:
    key =
    encoded_key = base64.b64encode(key)
    print(encoded_key.decode("ascii") )

or in Linux as:

cat KEYFILE | base64 -w 0

where you need to replace KEYFILE with the relevant filename (such as xxxxxxxxxx-certificate.pem.crt or xxxxxxxxxx-private.pem.key).

Further information


Sample Apps

A few sample apps to get started: