Skip to main content

Overview

Containers are lightweight, isolated environments that package everything needed to run an application — code, dependencies, and system libraries — so it behaves consistently across platforms. Advantages of containerization include:
  • Consistency: Containers provide a consistent environment across multiple platforms and operating systems. Developers can create images that transform into containers when deployed, ensuring uniformity regardless of the underlying infrastructure.
  • Automation: Containerization supports automated tasks. Developers can schedule actions without manual intervention, reducing repetitive work.
  • Stability: Containers are based on Linux, with the Linux kernel present in every container. This stability extends to systems and devices, including when containers run on macOS or Windows.
  • Space efficiency: Unlike traditional virtual machines, which consume significant server space and memory, containers include only application code and dependencies. They can run entirely in the cloud and minimize resource requirements.
Container architecture overview

IMSDK in Docker

This container targets QLI 2.x and later. QLI 1.x is not supported.
IMSDK in a Docker container means the full Qualcomm multimedia and AI software stack is delivered as containerized environments instead of being manually installed directly on a host or target system. In practice, a build container compiles and assembles all required components, including cross-compiled artifacts for Qualcomm ARM64 targets. The build then produces a deploy container that contains only the runtime pieces needed on the device. Key benefits of this approach:
  • Reproducible builds: Use the same toolchain, package versions, and scripts across developers and CI. Reduces “works on my machine” failures.
  • Build and runtime separation: Use a heavy build image to compile artifacts and a lean deploy image that contains only runtime essentials.
  • Efficient cross-compilation: Build on the host and target ARM64 Qualcomm devices reliably, faster than compiling directly on the target.
  • Secure runtime posture: Run as a non-root user by default, avoid broad privileged-container dependencies, and use CDI-based explicit hardware passthrough for GPU, camera, and media devices.
  • Operational consistency: Use versioned images that are testable, promotable, and rollback-friendly, with a predictable deployment flow using environment and CDI configuration on the target.

Features

  • Debian (Trixie) base: Built on Debian Trixie for a streamlined and lightweight foundation.
  • No platform dependencies: Entirely self-sufficient — no eSDK or host platform content required to build or deploy.
  • Self-contained dependencies: All necessary dependencies are installed directly inside the container, minimizing host-container compatibility issues and simplifying deployment on edge devices.
  • GStreamer 1.26: Includes GStreamer 1.26 with the latest gst-qti-oss-patches for optimized performance on Qualcomm platforms.
  • Broad target support: Validated across QLI 2.x, Ubuntu Server, and Ubuntu Desktop. Supports QCS615, QCS6490, IQ8, and IQ9.

Versions

VersionStatus
2.0.1Latest

Architecture

The following diagram explains the build and deployment flow of the IMSDK Docker image.
IMSDK Docker build and deployment flow
  • The host machine runs a development container that compiles all IMSDK sources and dependencies into Debian artifacts.
  • The generated artifacts are transferred to a Debian-based deploy container with CDI-managed hardware access that runs on the Qualcomm device.

Quick Start Guide

Before getting started, ensure the device is set up by following the Installation guide.
QIMSDK Docker is not supported on SELinux-enabled QLI builds.

Download configuration files

Before setting up the device, download the CDI and environment files for the target chipset and OS. The CDI JSON file provides platform-specific device mountings; the environment file sets the required variables inside the container. Select your chipset to download the corresponding files: Copy the downloaded files to the device:
scp <downloaded-cdi-file>.json <user>@<device-ip>:/etc/cdi/qimsdk.json
scp <downloaded-env-file>.env <user>@<device-ip>:/etc/docker/env/qimsdk.env

Set up and run on the device

1

SSH into the device

ssh <user>@<device-ip>
2

Create required directories

mkdir -p /etc/cdi/ /etc/docker/env/
mkdir -p /etc/media /etc/models /etc/labels && chmod -R 666 /etc/media /etc/models /etc/labels
3

Pull the container image

docker pull artifacts.codelinaro.org/iot-solutions-microservices/qimsdk:latest
Tag the image locally:
docker tag artifacts.codelinaro.org/iot-solutions-microservices/qimsdk:latest qimsdk:latest
4

Run the container

docker run -it -d --net host --env-file /etc/docker/env/qimsdk.env --device qualcomm.com/device=qimsdk -h qimsdk --name <desired-container-name> <generated-image-name>
After the container is running, execute a new terminal process in it:
docker exec -ti <desired-container-name> bash
Once inside the container, refer to the Examples section to run sample AI pipelines, multimedia pipelines, GStreamer C/C++ AI applications, or GStreamer C/C++ multimedia applications.
Gstreamer Python sample applications are not supported in this QIMSDK container.

Build an IMSDK container

All steps in this section are performed on a Ubuntu 22.04 or 24.04 machine which can be x86 or ARM64.

Prerequisites

Install prerequisite packages on the host (one time only).
sudo apt install -y jq tofrodos
sudo wget https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 -O /usr/bin/yq
sudo chmod +x /usr/bin/yq
Do not install yq via snap. If it was installed via snap, remove it and reinstall using the commands above:
sudo snap remove yq
sudo systemctl stop snapd
Increase the maximum user watches and instances on the host to prevent this error during development:
System limit for number of file watchers reached
Add these lines to /etc/sysctl.conf:
fs.inotify.max_user_instances=8192
fs.inotify.max_user_watches=542288
Save the file and reboot the host system.
To build Docker image files, the host system requires at least 64 GB of RAM and a swap image of at least half the available RAM plus a small reserve.For example, with 64 GB of RAM, the recommended swap size is at least 32 GB. With an 8 GB reserve, total RAM + swap + reserved memory is 104 GB.Check whether swap is enabled:
sudo swapon --show
If swap is missing or too small, create a new swap file:
MEM_AVAIL=$(grep MemAvailable /proc/meminfo | awk '{printf "%.0f\n", $2/1024/1024}')
let MEM_SWAP="$MEM_AVAIL / 2 + 8"
sudo swapoff /swap.img
sudo fallocate -l "${MEM_SWAP}G" /swap.img
sudo chmod 600 /swap.img
sudo mkswap /swap.img
sudo swapon /swap.img
Edit /etc/default/grub and set:
GRUB_CMDLINE_LINUX_DEFAULT="text cgroup_enable=memory swapaccount=1"
Update GRUB:
sudo update-grub
Edit /etc/fstab and add this line at the end of the file:
/swap.img none swap sw 0 0
Save and close all opened system files, then reboot the host system.After rebooting, verify the swap image matches the expected size:
sudo swapon --show
Docker must be configured on the host system once before building images.
Remove old Docker Desktop versions if present:
sudo apt remove docker-desktop
rm -r $HOME/.docker/desktop
sudo rm /usr/local/bin/com.docker.cli
sudo apt purge docker-desktop
Install repository dependencies:
sudo apt-get update
sudo apt-get install \
  ca-certificates \
  curl \
  gnupg \
  lsb-release
Create the Docker keyring directory:
sudo mkdir -p /etc/apt/keyrings
Add the Docker GPG key:
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
Add the Docker repository:
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli
Add the user to the Docker group to run Docker commands without sudo:
sudo groupadd docker
sudo usermod -aG docker $USER
newgrp docker
Until the host is rebooted, run newgrp docker in every newly opened terminal.
Tabs, unexpected spaces, and other invisible whitespace characters can break JSON configuration files and may cause a docker.service failed to start error.
Add the registry mirror configuration to /etc/docker/daemon.json:
{
  "registry-mirrors": ["<docker-registry-mirror-url>"]
}
Restart Docker:
sudo fromdos -f /etc/docker/daemon.json
sudo systemctl daemon-reload
sudo systemctl restart docker
If network downloads require a proxy, add the proxy configuration to the Docker daemon.
Tabs, unexpected spaces, and other invisible whitespace characters can break JSON configuration files and may cause a docker.service failed to start error.
Add the proxy configuration to /etc/docker/daemon.json:
{
  "proxies": {
    "http-proxy": "<http-proxy-url>",
    "https-proxy": "<https-proxy-url>",
    "no-proxy": "<no-proxy-url>"
  }
}
Restart Docker:
sudo fromdos -f /etc/docker/daemon.json
sudo systemctl daemon-reload
sudo systemctl restart docker
Set proxy environment variables before invoking Docker build commands:
export http_proxy=<http-proxy-url>
export https_proxy=<https-proxy-url>
export no_proxy=<no-proxy-url>
This ensures that both the Docker daemon and build processes use the same proxy settings as the host system.
Until the host is rebooted, run newgrp docker in every newly opened terminal.
Run the Docker hello-world container:
docker run hello-world
If the build image script fails with a No space left on device error, move the Docker directory to /local/mnt.
All containers must be restarted after moving the Docker directory because the Docker service is stopped during this procedure.
Stop Docker:
service docker stop
Verify that no Docker process is running:
ps faux
Check the Docker directory structure:
sudo ls /var/lib/docker/
Back up the current Docker directory:
tar -zcC /var/lib docker > /mnt/pd0/var_lib_docker-backup-$(date +%s).tar.gz
Move the Docker directory to the new partition:
mv /var/lib/docker /local/mnt/docker
Create a symlink to the Docker directory in the new partition:
ln -s /local/mnt/docker /var/lib/docker
Verify that the Docker directory structure is unchanged:
sudo ls /var/lib/docker/
Start Docker:
service docker start
List container names:
docker ps -a
Start a listed container:
docker start <container-name>
Check that there are no active cgroup CPU and memory utilization restrictions on the host. The Docker service cgroup memory configuration entries should have max set as the value.
sudo systemctl list-unit-files | grep -Ei docker
sudo systemctl status docker.service
cat /sys/fs/cgroup/system.slice/docker.service/
cat /sys/fs/cgroup/system.slice/docker.service/memory.max
cat /sys/fs/cgroup/system.slice/docker.service/memory.swap.max
If the host system has 64 GB RAM or less, tune system VM page swap and dentry and inode cache reclaim behavior.vm.swappinessHigher values cause more aggressive application memory page swapping; lower values favor keeping pages in memory longer. The default is usually 60 on recent Ubuntu and Debian versions, which may not fit the Docker build environment on systems with low available RAM. A value of 10 is recommended.vm.vfs_cache_pressureIntermittent high VFS pressure can result from creating many small temporary files during package installation, causing the OS to prematurely swap memory pages associated with cached dentries and inodes. The default is usually 100. Setting it to 400 relaxes cache management by freeing cached pages earlier, reducing the chance of running out of memory during high multi-threaded load.vm.min_free_kbytesIn heavy multi-stage Docker builds with many buildx threads, the host OS can exhaust available RAM quickly. The mandatory VM free memory watermark is controlled by vm.min_free_kbytes. Too low risks deadlocks under load; too high causes premature OOM kills. A value of 262144 KB better balances free memory under excessive load.Edit /etc/sysctl.conf and add:
vm.swappiness=10
vm.vfs_cache_pressure=400
vm.min_free_kbytes=262144

Build steps

1

Fetch the source code

Run the following commands on the host machine:
git clone https://git.codelinaro.org/clo/le/sdk-tools.git -b imsdk-tools.lnx.1.0
cd sdk-tools/qimsdk-debian/
git checkout 513e0a3fe1e67e131fa4f65694eeb07294ce8356
2

Build the container

Build qimsdk_deploy_arm64, which contains the minimal set of runtime binaries needed to execute GStreamer use cases:
docker build --build-arg QIMSDK_ARG_QNP_VERSION=<version, e.g. 2.47.0.260601> --target qimsdk_deploy_arm64 -t <desired-image-name> .
For example:
docker build --build-arg QIMSDK_ARG_QNP_VERSION=2.47.0.260601 --target qimsdk_deploy_arm64 -t qimsdk .
If QIMSDK_ARG_QNP_VERSION is not provided, the QNN and SNPE plugins are disabled in the image.
See qimsdk-debian/README.md for more information about the Docker image composition and workflow.
3

Save the image

Save the built image as a tar file:
docker save -o qimsdk.tar <generated-image-name>
4

Copy and load the image on the device

Copy the tar file to the device:
scp qimsdk.tar <user>@<device-ip>:$HOME/qimsdk.tar
Load the image on the device:
docker load -i $HOME/qimsdk.tar
On a QLI device $HOME is /root; on a Ubuntu device it is /home/ubuntu.
5

Run the container on the device

With the image copied to the device, follow the Set up and run on the device steps to configure CDI and start the container.

Install additional packages inside the container

Use this procedure to install packages into an already running qimsdk-debian deploy container. The container must have been started with the --net host option. The following steps use Python as an example.
1

Execute a shell in the container as root

docker exec -ti qimsdk sh
2

Install apt packages

In the root shell, install the required packages:
apt-get update
DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC apt-get install -y --no-install-recommends \
  python3 python3-pip python3-venv python3-gi gobject-introspection \
  libgirepository-2.0-dev
apt -y upgrade
apt-get autoremove -y
apt-get clean
3

Install pip packages

Create a Python virtual environment and install the required pip packages:
python3 -m venv --system-site-packages /home/qimsdk/.venv
/home/qimsdk/.venv/bin/pip install PyGObject opencv-python
4

Activate the virtual environment automatically (optional)

Add the virtual environment activation to shell startup files to avoid sourcing it manually in every new shell:
echo "source /home/qimsdk/.venv/bin/activate" >> /root/.bash_aliases
echo "source ~/.venv/bin/activate" >> /home/qimsdk/.bash_aliases
chown -R qimsdk:qcom /home/qimsdk/.bash_aliases
After setup is complete, Python examples can be run from each new shell executed as the qimsdk user:
docker exec -ti qimsdk bash
If automatic activation was not configured, run this command in every new shell executed as the qimsdk user inside the container:
source ~/.venv/bin/activate

Debugging

The following techniques apply to the QIMSDK container running on the Qualcomm device.

Check container logs

1

Stream live logs

docker logs -f <container-name>
2

Print the last 100 lines and follow

docker logs --tail 100 -f <container-name>

Inspect container state

1

List all containers and their status

docker ps -a
2

Inspect container metadata

Inspect detailed container metadata including environment variables, mounts, and network settings:
docker inspect <container-name>

Restart a container

1

Stop the container

docker stop <container-name>
2

Start the container

docker start <container-name>