Skip to content

Docker Container OTA Update

This tutorial walks you through building and updating an IoT application packaged as a Docker container on a fleet of IoT devices, using the SocketXP OTA update feature end-to-end.

Before you start, make sure you have downloaded and installed the SocketXP agent on your IoT devices and completed the Getting Started guide.

For the sample workflow script used here, see Docker Container OTA Update Workflow Script.

Sample App

We'll use a simple C program to demonstrate the OTA update workflow, and package the compiled binary as a Docker container image. The app prints "Hello, OTA update!" every 10 minutes and runs as a Linux systemd service on the device.

Note

We're using C for this example, but the app can be written in any language, such as Java, C++, Go, Python or JavaScript.

/*
 * To build: gcc myapp.c -o myapp
 * To run: ./myapp
 * Output: "Hello, OTA update!"
 */

#include <stdio.h>
#include <unistd.h>

int main() {
    while (1) {
        printf("Hello, OTA update!\n");
        fflush(stdout); // Ensure immediate output
        sleep(600); // in seconds
    }
    return 0;
}

The C program, build script, and Dockerfile used in this tutorial are available in our official GitHub repository.

Build the App Binary

Clone the repository:

$ git clone https://github.com/ampaslabs/ota-update-build-artifacts

For this example, we'll use the container folder:

~/$ cd ota-update-build-artifacts/container
~/ota-update-build-artifacts/container$ ls
myapp  update.sh
~/ota-update-build-artifacts/container$ ls myapp/
Dockerfile  makefile  myapp.c

The container folder contains:

  1. A myapp folder containing the app source (myapp.c), a makefile to compile the app, and a Dockerfile to build the Docker container. The makefile builds a Docker image using the Dockerfile and pushes the image to the DockerHub registry using credentials you provide.
  2. An update.sh shell script — the workflow script that runs on the target device and updates the myapp Docker container.

Note

There's no make_artifact.sh script for this example, because we don't build a tar.gz artifact using the Docker image. The Docker image is pushed to a third-party container registry (e.g. DockerHub). We'll upload just the update.sh workflow script to the SocketXP Artifact Registry as a script type artifact, and deploy container OTA updates using the workflow script.

Create a New Version of the App

Edit myapp.c to print "Hello, OTA update! Version 1.0.0" — we'll call this version 1.0.0 of the app.

/*
 * To build: gcc myapp.c -o myapp
 * To run: ./myapp
 * Output: "Hello, OTA update!"
 */

#include <stdio.h>
#include <unistd.h>

int main() {
    while (1) {
        printf("Hello, OTA update! Version 1.0.0\n");
        fflush(stdout); // Ensure immediate output
        sleep(600); // in seconds
    }
    return 0;
}

Build the app and package it into a Docker container image using make all:

~/ota-update-build-artifacts/container$ cd myapp
~/ota-update-build-artifacts/container/myapp$ make all
gcc -o myapp myapp.c
docker build -t test-user/myapp:1.0.0 .
[+] Building 0.9s (8/8) FINISHED                                                     docker:default
 => [internal] load build definition from Dockerfile                                            0.0s
 => => transferring dockerfile: 284B                                                             0.0s
 => [internal] load metadata for docker.io/library/ubuntu:latest                                 0.9s
...
~/ota-update-build-artifacts/container/myapp$ ls
Dockerfile  makefile  myapp  myapp.c
~/ota-update-build-artifacts/container/myapp$ docker image ls
REPOSITORY           TAG       IMAGE ID       CREATED             SIZE
test-user/myapp      1.0.0     28a75833fed7   5 minutes ago      101MB

Log in to your DockerHub account and verify that the new Docker image version 1.0.0 was pushed to the registry.

Workflow Script - update.sh

The OTA update workflow script contains all the instructions required to update the myapp Docker container running on the IoT devices.

~/ota-update-build-artifacts/container$ cat update.sh
#!/bin/bash

#================================================
# MyApp Container Update Workflow Script
#================================================

DOCKER_USER="dockerhub-username"
DOCKER_REPO="myapp"
NEW_VERSION="1.0.0"
OLD_VERSION="0.0.1"
CONTAINER_NAME="myapp"


# Function to check if a container is running and healthy
check_container_status() {
    local container_id="$1"
    local status=$(docker inspect --format='{{.State.Health.Status}}' "$container_id" 2>/dev/null)
    local state=$(docker inspect --format='{{.State.Running}}' "$container_id" 2>/dev/null)

    if [[ "$state" == "true" ]]; then
        if [[ -z "$status" ]]; then
            return 0 # Running, no healthcheck
        elif [[ "$status" == "healthy" ]]; then
            return 0 # Running and healthy
        else
            return 1 # Running, but unhealthy
        fi
    else
        return 1 # Not running
    fi
}

# Function to rollback to the previous version
rollback() {
    echo "Rolling back to version $OLD_VERSION..."

    # Stop and remove the new container
    docker stop "${CONTAINER_NAME}-${NEW_VERSION}" 2>/dev/null
    docker rm "${CONTAINER_NAME}-${NEW_VERSION}" 2>/dev/null

    # Delete the new image
    docker image rm "$DOCKER_USER/$DOCKER_REPO:$NEW_VERSION" 2>/dev/null

    # Run the old container
    docker start "${CONTAINER_NAME}-${OLD_VERSION}" 2>/dev/null
}

# Main update process
echo "Updating to version $NEW_VERSION..."

# Pull the new image
docker pull "$DOCKER_USER/$DOCKER_REPO:$NEW_VERSION"

# Stop the old container
docker stop "${CONTAINER_NAME}-${OLD_VERSION}" 2>/dev/null

# Run the new container
docker run -d --name ${CONTAINER_NAME}-${NEW_VERSION} "$DOCKER_USER/$DOCKER_REPO:$NEW_VERSION"

# Check container status
sleep 30 # Give the container time to start and healthcheck to run. Adjust as needed.
container_id=$(docker ps -q --filter name=${CONTAINER_NAME}-${NEW_VERSION})

if [[ -n "$container_id" ]] && check_container_status "$container_id"; then
    echo "Update successful!"
    # Clean up the old container and image
    docker rm ${CONTAINER_NAME}-${OLD_VERSION} 2>/dev/null
    docker image rm "$DOCKER_USER/$DOCKER_REPO:$OLD_VERSION" 2>/dev/null
else
    echo "Update failed! Rolling back..."
    rollback
    echo "Rollback complete."
fi

What the script does:

  • Downloads the new version (1.0.0) of the myapp Docker container image.
  • Stops the old version (0.0.1) of the myapp container already running on the device, keeping it as a backup rather than removing it.
  • Creates a new container named myapp-1.0.0 from the newly downloaded image.
  • Verifies the new container is running successfully. If so, it removes the old container.
  • If the new container fails to start, the script removes it and restores the old container from backup.

Upload the Artifact

We'll upload the update.sh workflow script directly to the SocketXP Artifact Registry as a script type artifact.

Log in to your SocketXP account and go to the OTA Update page. In the Artifacts table, click Upload new artifact.

SocketXP IoT OTA Update - Update IoT app Docker container on remote embedded Linux devices

Select artifact type script. Browse and select the update.sh file. Specify the artifact version, 1.0.0 in this example.

Specify the destination folder on the target device where the script should be downloaded and executed, for example: /tmp or /home/user/myapp or /usr/bin.

Specify the command to execute the script on the target device, for example: /bin/bash update.sh.

Click Upload to upload the artifact to the cloud registry. You'll see a message saying "File uploaded successfully".

Deploy the Artifact

SocketXP OTA Update upload an IoT app binary to SocketXP cloud artifact registry

From the Artifacts table, select the artifact you just uploaded.

Note

If you don't see your artifact yet, click Refresh to reload the table data.

Click the + icon next to the artifact to create a new deployment.

SocketXP OTA Update - deploy and update an IoT app as Docker container on a fleet of remote iot and embedded Linux devices

Give the deployment a name, for example, deploy-version-1.0.0-to-test-devices. Specify the target device ID, device group, or tag to deploy the artifact on.

Note

You can deploy the artifact on a single device ID, device group, or device tag. To deploy on more than one group or tag, repeat the "Create New Deployment" process for each.

Click Create Deployment. Go to the Deployments tab and click Refresh.

SocketXP OTA Update - View the summary of Docker Container OTA updates deployed

Select the deployment to see its progress. Click More Info < > to monitor the progress on each target device in the group or tag. Click Refresh to view the progress.

SocketXP OTA Update - View the progress of Docker Container OTA updates deployed on each IoT or embedded Linux device

You can check the stdout and stderr logs generated by the update process by clicking the view log buttons.

Verify the Update

Log in to one of the devices to check if the myapp deployment was successful:

$ docker ps
CONTAINER ID   IMAGE                      COMMAND            CREATED          STATUS          PORTS     NAMES
cdc95577be33   test-user/myapp:1.0.0      "/usr/bin/myapp"   5 minutes ago    Up 5 minutes              myapp-1.0.0

You can also view the app logs using docker logs:

$ docker logs myapp-1.0.0
Hello, OTA update! Version 1.0.0

You have successfully updated the myapp Docker container on remote IoT devices using SocketXP OTA update.

Next Steps

Now that you've learned how to publish a Docker container as an OTA update, check out the other artifact types: