Skip to main content

Add an IoT Edge Device on Ubuntu

On Ubuntu the Azure IoT Edge runtime runs natively on the host. No virtual machine is involved, unlike the Windows with EFLOW setup.

The installation script install-iot-edge.sh prepares everything the device needs to enroll: the Microsoft package repository, the container engine, the IoT Edge runtime, the volume directories and the provisioning configuration. No manual module configuration is required. Broker, storage, certificate and telemetry settings are delivered to the device by the Datalogic Connect deployment manifest when the device is enrolled.

Prerequisites

Before you begin

  1. Create an Edge Device in Device Visibility. The Serial Number you enter here becomes the Edge Device ID. The Device IP must be the static IP address of the Ubuntu host, because the ProvisioningModule issues the server certificate for that address.

  2. Make sure the host meets the hardware requirements for the number of devices it will manage.

  3. Make sure you have superuser privileges on the host. The installation must run as root.

  4. Open the device's Overview page in Connect. From here you download the installation script and, if you need it, the credentials file.

If this is your first time using Connect

Start with Authentication.

Provisioning values

The installer needs only the following three values. When you download the script from the device's Overview page, Connect embeds all three, so you normally do not have to supply them yourself.

ValueWhere it comes fromWhere it is used
Scope IDscope in the Get Credentials JSON exportDPS id_scope in /etc/aziot/config.toml
Primary KeyprimaryKey in the Get Credentials JSON exportDPS symmetric_key in /etc/aziot/config.toml
Edge Device IDThe Serial Number you entered when creating the device. It is not part of the export.DPS registration_id in /etc/aziot/config.toml
Migrating from earlier versions

Earlier versions of this procedure also required the Organization ID, the SAS Devices Key, the SAS Smart Enrollment Key, the SAS Edge Storage Key, a certificate common name and a fixed server IP address. None of them is needed on the device anymore. The backend resolves them and delivers the resulting module configuration through the deployment manifest at enrollment time. For the same reason the installer no longer accepts --hostname and --ip.

Supported OS

OSStatus
Ubuntu 22.04 LTS (Jammy Jellyfish)✅ Verified
Ubuntu 24.04 LTS (Noble Numbat)✅ Verified
OS compatibility

The installation script exits on any other distribution or Ubuntu release. Make sure the host runs one of the versions listed above before you start.

Network requirements

The edge device must be able to reach the following outbound endpoints:

EndpointPortProtocolPurpose
*.azure-devices.net8883, 443, 5671MQTT TLS, AMQPS, or HTTPSSend diagnostic data and receive cloud commands (edgeHub upstream)
global.azure-devices-provisioning.net443HTTPSDevice provisioning
*.blob.core.windows.net443HTTPSFile upload and Docker image pull
crsolinfraprodeuw.azurecr.io443HTTPSContainer registry
crsolinfraprodeuw.westeurope.data.azurecr.io443HTTPSContainer registry (data endpoint)
*.azureiotcentral.com443HTTPSDevice Smart Enrollment
Firewall configuration

If the device is behind a corporate firewall, allow all the outbound endpoints above. The IoT Edge runtime selects the best available upstream protocol, MQTT TLS on port 8883 or HTTPS on port 443. Field devices also need to reach the edge device on the local network. If a host firewall such as ufw is active, allow the inbound ports listed in Network Requirements.


Quick install

Use this path for the automated setup. The script installs the container engine and the IoT Edge runtime, creates the volume directories and provisions the device against Azure DPS.

1. Download the installation archive

Open the device's Overview page in Connect, click Install script and choose Linux. The archive contains install-iot-edge.sh, its SHA-256 checksum and a README.

Copy the archive to the device, extract it and verify the checksum:

tar -xzf <archive-name>.tar.gz
sha256sum -c install-iot-edge.sh.sha256

2. Run the installer

The script downloaded from Connect already contains the Scope ID, the Edge Device ID and the Primary Key of the device, so no option is required:

sudo bash install-iot-edge.sh

The script prints a summary of the resolved values and asks Proceed with installation? [y/N]. Answer y to start.

Pre-cleanup

The installer can be re-run, but its first step deletes the whole content of /srv. Do not run it on a device that holds data you need under that path, and do not store custom data under /srv.

Supplying the values yourself

If the script does not contain the provisioning values, for example because you obtained it from another source, you can supply them in three ways.

  • Credentials file next to the script. Download the Get Credentials JSON export from the device's Overview page, save it as edge-credentials.json in the same folder as the script and run the script without options. The script detects the file and prompts only for the serial number.

  • Credentials file in another location. Point the script at the file and pass the serial number:

    sudo bash install-iot-edge.sh \
    --config "/path/to/credentials.json" \
    --serial-number "<SERIAL_NUMBER>"
  • All three values on the command line.

    sudo bash install-iot-edge.sh \
    --scope-id "<SCOPE_ID>" \
    --serial-number "<SERIAL_NUMBER>" \
    --primary-key "<PRIMARY_KEY>"

The credentials export looks like this. Pass it exactly as downloaded:

{
"createdAt": "2026-09-02T15:04:14.989Z",
"data": [
{ "label": "scope", "value": "0ne0XXXXXXX" },
{ "label": "primaryKey", "value": "<PRIMARY_KEY>" }
]
}

The export carries the Scope ID and the Primary Key, but not the Edge Device ID. Anything still missing after all sources are merged is asked interactively.

Parameter table

The installation script accepts the following options. Run sudo bash install-iot-edge.sh --help on the device to print the same list.

OptionDescriptionNotes
--configPath to the Get Credentials JSON export. Supplies the Scope ID and the Primary Key.--json is an alias. If omitted, edge-credentials.json next to the script is used.
--serial-numberEdge Device ID, that is the Serial Number of the device.Not part of the credentials export. --device-id is an alias.
--scope-idDPS ID Scope.Overrides the value read from the credentials file.
--primary-keyDPS symmetric primary key.Overrides the value read from the credentials file.
--helpPrint the usage message and exit.

The sources are merged. Each value is taken from the highest-ranked source that supplies it:

  1. --scope-id, --serial-number and --primary-key on the command line
  2. The values embedded by Connect at download time
  3. The JSON export passed with --config
  4. An edge-credentials.json file next to the script
  5. The configuration saved by a previous run in /etc/connect/install-edge.env
  6. Interactive prompts, for anything still missing
Saved configuration

After collecting the values, the script saves them to /etc/connect/install-edge.env with mode 600, because the file contains the primary key. On a later run without command-line values or a credentials file, the script shows the saved configuration and offers to reuse it. In every other case the saved file is used only as a fallback for the values that are still missing.

3. What the script does

  1. Pre-cleanup. Stops the IoT Edge service, removes any existing edge-agent container and wipes /srv/*, so a re-run always starts from a clean state.
  2. System update. Runs apt-get update and apt-get upgrade.
  3. Runtime installation. Registers the Microsoft package repository for the detected Ubuntu release, then installs moby-engine (or docker.io if moby-engine is unavailable) and aziot-edge. Packages already present are skipped.
  4. Docker daemon configuration. Writes /etc/docker/daemon.json with the local log driver and a rolling policy of 5 files of 10 MB, then enables and restarts the Docker service so that the policy applies from this run onward.
  5. Directory structure. Creates the volume directories bound by the deployment manifest and applies the ownership each container image expects. See the directory table in the manual procedure.
  6. Provisioning configuration. Writes /etc/aziot/config.toml with the DPS symmetric-key provisioning block, the image garbage collection settings and the bootstrap edgeAgent image.
  7. Apply. Runs iotedge config apply.

The full run is logged to /var/log/install-edge.log.

4. Enroll the device

When the script finishes, wait a few minutes before enrolling the edge device in the Connect portal. IoT Edge must complete the DPS provisioning and start edgeAgent before it can receive the deployment manifest. Enrolling too early can leave the modules unconfigured.

Check that edgeAgent is running before you enroll:

sudo iotedge list

After the enrollment, follow Verify the installation.


Manual procedure

Use this path when you want full control over each step. It produces the same result as the installation script.

1. Update the system and add the Microsoft package repository

Update the system packages:

sudo apt-get update
sudo apt-get upgrade -y

Register the Microsoft package repository for the running Ubuntu release:

. /etc/os-release
wget "https://packages.microsoft.com/config/ubuntu/${VERSION_ID}/packages-microsoft-prod.deb" -O packages-microsoft-prod.deb
sudo dpkg -i packages-microsoft-prod.deb
rm packages-microsoft-prod.deb

2. Install the container engine

Install the Moby engine:

sudo apt-get update
sudo apt-get install -y moby-engine
note

If moby-engine is not available in the configured repositories, install docker.io instead. The installation script applies the same fallback.

Create /etc/docker/daemon.json with the local logging driver and an explicit log rolling policy, then enable and restart the Docker service:

sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json > /dev/null <<'EOF'
{
"log-driver": "local",
"log-opts": { "max-size": "10m", "max-file": "5" }
}
EOF
sudo systemctl enable docker
sudo systemctl restart docker

3. Install the IoT Edge runtime

sudo apt-get update
sudo apt-get install -y aziot-edge

4. Create the volume directories

The deployment manifest binds the following host directories into the modules. Create them and set the ownership expected by the user account each container runs as.

sudo mkdir -p \
/srv/redis \
/srv/MqttTranslationModule/log /srv/MqttTranslationModule/certs \
/srv/DeviceHubModule/log /srv/DeviceHubModule/certs \
/srv/StorageModule/log \
/srv/ProvisioningModule/log \
/srv/shared/download /srv/shared/certs /srv/shared/certs/server \
/srv/edgeAgent /srv/edgeHub \
/srv/DockerRegistry/registry \
/srv/blobstorage/scan_avoidance /srv/blobstorage/ticket_swithing /srv/blobstorage/produce_recognition
sudo chown 1000 \
/srv/redis \
/srv/MqttTranslationModule/log /srv/MqttTranslationModule/certs \
/srv/DeviceHubModule/log /srv/DeviceHubModule/certs \
/srv/StorageModule/log \
/srv/ProvisioningModule/log \
/srv/shared/download \
/srv/edgeAgent /srv/edgeHub \
/srv/DockerRegistry/registry

sudo chown 1001 /srv/shared/certs /srv/shared/certs/server
sudo chmod 755 /srv/shared/certs /srv/shared/certs/server

sudo chown -R 11000:11000 /srv/blobstorage
sudo chmod -R 755 /srv/blobstorage
UIDDirectoriesUsed by
1000/srv/redis, /srv/MqttTranslationModule/*, /srv/DeviceHubModule/*, /srv/StorageModule/log, /srv/ProvisioningModule/log, /srv/shared/download, /srv/edgeAgent, /srv/edgeHub, /srv/DockerRegistry/registryGateway modules, Redis, edgeAgent and edgeHub storage, registry data
1001/srv/shared/certs, /srv/shared/certs/serverShared certificates, written by ProvisioningModule and read by nanomq
11000/srv/blobstorage and its scan_avoidance, ticket_swithing and produce_recognition subdirectoriesLocal blob storage of the Smart Vision Suite for Retail manifest
Smart Vision Suite for Retail manifest

The /srv/blobstorage directories and /srv/DockerRegistry/registry are used only by the Smart Vision Suite for Retail manifest (ROGER). Creating them on a base edge gateway adds only a few empty directories and keeps a single procedure valid for both manifests. The ticket_swithing spelling is the one used by that manifest. The host path must match it exactly, otherwise the bind mount points to a different directory.

5. Configure IoT Edge provisioning

Edit the IoT Edge configuration file:

sudo nano /etc/aziot/config.toml

Set the following content. Replace <SCOPE_ID>, <SERIAL_NUMBER> and <PRIMARY_KEY> with your provisioning values.

[provisioning]
source = "dps"
global_endpoint = "https://global.azure-devices-provisioning.net"
id_scope = "<SCOPE_ID>"

[provisioning.attestation]
method = "symmetric_key"
registration_id = "<SERIAL_NUMBER>"
symmetric_key = { value = "<PRIMARY_KEY>" }

[image_garbage_collection]
enabled = true
cleanup_recurrence = "1d"
image_age_cleanup_threshold = "7d"
cleanup_time = "00:00"

[agent]
name = "edgeAgent"
type = "docker"

[agent.config]
image = "crsolinfraprodeuw.azurecr.io/azureiotedge-agent:1.5.40"

6. Apply the configuration

sudo iotedge config apply

Wait until sudo iotedge list reports edgeAgent as running, then enroll the edge device in the Connect portal and follow Verify the installation.


Verify the installation

After the enrollment, Connect applies the deployment manifest and the modules are pulled. Wait at least two minutes, then list the IoT Edge modules:

sudo iotedge list

All modules should reach the running state:

NAME                   STATUS      DESCRIPTION      CONFIG
DeviceHubModule running Up 2 hours crsolinfraprodeuw.azurecr.io/device-hub-module:2.0.11-amd64.debug
MqttTranslationModule running Up 2 hours crsolinfraprodeuw.azurecr.io/mqtt-translation-module:1.0.0-amd64.debug
ProvisioningModule running Up 2 hours crsolinfraprodeuw.azurecr.io/provisioning-module:1.0.1-amd64.debug
StorageModule running Up 2 hours crsolinfraprodeuw.azurecr.io/storage-module:1.1.1-amd64.debug
blobstoragepr running Up 2 hours crsolinfraprodeuw.azurecr.io/azure-blob-storage:1.4
blobstoragesa running Up 2 hours crsolinfraprodeuw.azurecr.io/azure-blob-storage:1.4
blobstoragets running Up 2 hours crsolinfraprodeuw.azurecr.io/azure-blob-storage:1.4
edgeAgent running Up 2 hours crsolinfraprodeuw.azurecr.io/azureiotedge-agent:1.5.40
edgeHub running Up 2 hours crsolinfraprodeuw.azurecr.io/azureiotedge-hub:1.5.40
nanomq running Up 2 hours crsolinfraprodeuw.azurecr.io/nanomq:0.24.14-slim
otelcollector running Up 2 hours crsolinfraprodeuw.azurecr.io/opentelemetry-collector-contrib:0.155.0
redis running Up 2 hours crsolinfraprodeuw.azurecr.io/redis:8.8.0-alpine
registry running Up 2 hours crsolinfraprodeuw.azurecr.io/registry:3
Module set

The module set and the image tags come from the manifest selected as Active manifest when the device was created, so they differ between manifests. The listing above belongs to the Smart Vision Suite for Retail manifest (ROGER). A base edge gateway does not run the blobstorage* and registry modules.

If a module stays in failed or keeps restarting, inspect it:

sudo iotedge logs <module-name>

The full installation log is available at /var/log/install-edge.log.