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
-
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.
-
Make sure the host meets the hardware requirements for the number of devices it will manage.
-
Make sure you have superuser privileges on the host. The installation must run as
root. -
Open the device's Overview page in Connect. From here you download the installation script and, if you need it, the credentials file.
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.
| Value | Where it comes from | Where it is used |
|---|---|---|
| Scope ID | scope in the Get Credentials JSON export | DPS id_scope in /etc/aziot/config.toml |
| Primary Key | primaryKey in the Get Credentials JSON export | DPS symmetric_key in /etc/aziot/config.toml |
| Edge Device ID | The Serial Number you entered when creating the device. It is not part of the export. | DPS registration_id in /etc/aziot/config.toml |
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
| OS | Status |
|---|---|
| Ubuntu 22.04 LTS (Jammy Jellyfish) | ✅ Verified |
| Ubuntu 24.04 LTS (Noble Numbat) | ✅ Verified |
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:
| Endpoint | Port | Protocol | Purpose |
|---|---|---|---|
*.azure-devices.net | 8883, 443, 5671 | MQTT TLS, AMQPS, or HTTPS | Send diagnostic data and receive cloud commands (edgeHub upstream) |
global.azure-devices-provisioning.net | 443 | HTTPS | Device provisioning |
*.blob.core.windows.net | 443 | HTTPS | File upload and Docker image pull |
crsolinfraprodeuw.azurecr.io | 443 | HTTPS | Container registry |
crsolinfraprodeuw.westeurope.data.azurecr.io | 443 | HTTPS | Container registry (data endpoint) |
*.azureiotcentral.com | 443 | HTTPS | Device Smart Enrollment |
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.
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.jsonin 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.
| Option | Description | Notes |
|---|---|---|
--config | Path 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-number | Edge Device ID, that is the Serial Number of the device. | Not part of the credentials export. --device-id is an alias. |
--scope-id | DPS ID Scope. | Overrides the value read from the credentials file. |
--primary-key | DPS symmetric primary key. | Overrides the value read from the credentials file. |
--help | Print the usage message and exit. |
The sources are merged. Each value is taken from the highest-ranked source that supplies it:
--scope-id,--serial-numberand--primary-keyon the command line- The values embedded by Connect at download time
- The JSON export passed with
--config - An
edge-credentials.jsonfile next to the script - The configuration saved by a previous run in
/etc/connect/install-edge.env - Interactive prompts, for anything still missing
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
- 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. - System update. Runs
apt-get updateandapt-get upgrade. - Runtime installation. Registers the Microsoft package repository for the detected Ubuntu release, then installs
moby-engine(ordocker.ioifmoby-engineis unavailable) andaziot-edge. Packages already present are skipped. - Docker daemon configuration. Writes
/etc/docker/daemon.jsonwith thelocallog 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. - 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.
- Provisioning configuration. Writes
/etc/aziot/config.tomlwith the DPS symmetric-key provisioning block, the image garbage collection settings and the bootstrapedgeAgentimage. - 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
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
| UID | Directories | Used by |
|---|---|---|
1000 | /srv/redis, /srv/MqttTranslationModule/*, /srv/DeviceHubModule/*, /srv/StorageModule/log, /srv/ProvisioningModule/log, /srv/shared/download, /srv/edgeAgent, /srv/edgeHub, /srv/DockerRegistry/registry | Gateway modules, Redis, edgeAgent and edgeHub storage, registry data |
1001 | /srv/shared/certs, /srv/shared/certs/server | Shared certificates, written by ProvisioningModule and read by nanomq |
11000 | /srv/blobstorage and its scan_avoidance, ticket_swithing and produce_recognition subdirectories | Local blob storage of the 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
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.