Skip to content

RBLN Container Toolkit

The RBLN Container Toolkit enables container runtimes to access Rebellions NPU devices using the Container Device Interface (CDI) specification. It automatically discovers host RBLN libraries and tools, generates CDI specs, and configures your container runtime, enabling containers to use NPU hardware with zero application changes.

Scope

The Container Toolkit is currently responsible only for CDI spec generation and runtime configuration. It exposes RBLN libraries and tools (such as rbln-smi) to containers via CDI. For NPU device allocation using RSD groups, see the NPU Allocation guide. These features will be integrated into a unified toolkit in a future release.

How It Works

                          ┌──────────────────────────────────────────┐
  Host System             │           RBLN Container Toolkit         │
 ─────────────            │                                          │
                          │  1. Discover    RBLN libs & tools        │
  /usr/lib64/             │       ↓         on the host              │
    librbln-*.so ────────►│  2. Generate    CDI spec (rbln.yaml)     │
  /usr/bin/               │       ↓                                  │
    rbln-smi ────────────►│  3. Configure   container runtime        │
                          │       ↓         (containerd/crio/docker) │
                          │  4. Run Hook    to update ldcache        │
                          │                 inside containers        │
                          └─────────────────────┬────────────────────┘
                                                │
                                                ▼
                          ┌──────────────────────────────────────────┐
  Container               │  $ docker run --device rebellions.ai/    │
                          │      npu=all my-app                      │
                          │                                          │
                          │  ✓ RBLN libraries mounted                │
                          │  ✓ Tools available (rbln-smi)            │
                          │  ✓ ldcache updated automatically         │
                          └──────────────────────────────────────────┘

The toolkit provides three binaries:

Binary Role
rbln-ctk Main CLI — generates CDI specs, configures runtimes, inspects the system
rbln-ctk-daemon Kubernetes daemon — automated setup with health endpoints and graceful shutdown
rbln-cdi-hook OCI hook — runs inside containers to update ldcache and create symlinks

The toolkit ships in two distribution flavors with intentionally different runtime profiles:

Flavor Target Device injection Refresh path
DEB / RPM package Standalone Docker hosts rbln-ctk injects /dev/rbln* and the matching /dev/rsd* group node via CDI. NPU↔RSD mapping is resolved through librbln-ml, which the package pulls in as a runtime dependency. Host-side rbln-cdi-refresh.path systemd unit, auto-enabled by postinstall. See Systemd Integration.
Container image Kubernetes DaemonSet rbln-ctk-daemon skips NPU device-node emission — device-plugin (or DRA) owns per-Pod /dev/rbln* allocation. Because the NPU↔RSD resolver is never invoked in this mode, the image is built without a librbln-ml dependency. The RDS class is emitted regardless of this gate — see RDS Device Injection. In-process loop inside rbln-ctk-daemon. See Automatic CDI Refresh.

Prerequisites

OS Architecture Container Runtime
Ubuntu 22.04/24.04 x86_64 containerd, CRI-O, Docker
RHEL 9.6/9.8 x86_64 containerd, CRI-O, Docker

Minimum runtime versions and how CDI is enabled on each:

Runtime Minimum version CDI activation
containerd 1.7 On by default from 2.0. Earlier versions are configured by rbln-ctk runtime configure, which sets enable_cdi and cdi_spec_dirs.
CRI-O 1.23.2 Always on. There is no enable_cdi toggle — injection is driven solely by cdi_spec_dirs, whose default already covers /var/run/cdi.
Docker 25.0.0 On by default from 28.2.0. Versions 25.0.0 through 28.1.x need the features.cdi key in daemon.json, which rbln-ctk runtime configure writes.

These minimums are the releases where CDI injection becomes available, not validated versions. For the containerd and CRI-O versions each RCNS release is validated against, see the RBLN Cloud Native Stack (RCNS) table in the Support Matrix.

  • RBLN driver — install it on the host before the container toolkit. The toolkit's DEB / RPM package declares the driver's UMD library as a hard runtime dependency (librbln-ml3 on Debian/Ubuntu, librbln-ml on RHEL-family hosts), so installing the toolkit on a host without the driver fails with an unmet-dependency error from apt-get or dnf.

Installation

Ubuntu / Debian

  1. Add the Rebellions official GPG key (skip if already configured):

    1
    2
    3
    4
    5
    $ sudo apt-get update
    $ sudo apt-get install ca-certificates curl
    $ sudo install -m 0755 -d /etc/apt/keyrings
    $ sudo curl -fsSL https://nexus.rebellions.ai/repository/raw-public/rebellions.asc -o /etc/apt/keyrings/rebellions.asc
    $ sudo chmod a+r /etc/apt/keyrings/rebellions.asc
    
  2. Add the repository to APT sources (skip if already configured):

    1
    2
    3
    $ echo \
      "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/rebellions.asc] https://nexus.rebellions.ai/repository/apt-public/ stable main" | \
      sudo tee /etc/apt/sources.list.d/rebellions-apt-public.list > /dev/null
    
  3. Update APT and install:

    $ sudo apt-get update
    $ sudo apt-get install rbln-container-toolkit
    

RHEL

  1. Register the Rebellions yum repository (skip if already configured):

    1
    2
    3
    4
    5
    6
    7
    $ sudo tee /etc/yum.repos.d/rebellions.repo > /dev/null <<EOF
    [rebellions-stable]
    name=Rebellions Stable
    baseurl=https://nexus.rebellions.ai/repository/yum-public/stable/x86_64/Packages/
    enabled=1
    gpgcheck=0
    EOF
    
  2. Refresh the cache and install:

    $ sudo dnf clean all
    $ sudo dnf install -y rbln-container-toolkit
    

Unsigned RPM packages

The current RPM packages are not yet signed, so gpgcheck=0 is required to install them. Switch the entry back to gpgcheck=1 once signed packages become available.

Quick Start

The quickest way to enable NPU access in containers:

# 1. Generate CDI specification (discovers RBLN libraries on host)
$ sudo rbln-ctk cdi generate

# 2. Configure your container runtime for CDI support
$ sudo rbln-ctk runtime configure -r docker -y

# 3. Restart the runtime so the new configuration takes effect
$ sudo systemctl restart docker

# 4. Run a container with NPU access (all NPUs)
$ docker run --device rebellions.ai/npu=all -it ubuntu:22.04

rbln-ctk runtime configure asks for confirmation before it touches the config; -y answers it, which is what keeps the block above safe to paste as a whole. It edits the runtime config but does not restart the runtime, so step 3 is required before the first docker run. Docker 28.2.0 and later enable CDI by default, so steps 2 and 3 can be skipped there. To expose only a subset of the host's NPUs, see Device Selection.

Name the runtime with -r on multi-runtime hosts

rbln-ctk runtime configure picks its target by probing runtime sockets. When it finds more than one, it does not guess — it stops with:

Error: detect runtime: multiple runtimes detected: [containerd, docker]. Use --runtime flag to specify which one to use (specify --runtime manually)

Docker hosts usually hit this, because Docker's embedded containerd exposes a socket of its own. Name the target explicitly, as in sudo rbln-ctk runtime configure -r docker.

Verify Setup

1
2
3
4
5
6
7
8
# Check what was discovered on the host
$ rbln-ctk cdi list

# View detected runtime and configuration
$ rbln-ctk info

# Use NPU tools inside a container
$ docker run --device rebellions.ai/npu=all -it ubuntu:22.04 rbln-smi

Preview Before Applying

cdi generate and runtime configure — the two subcommands that write — support --dry-run to see what would change without modifying anything. runtime configure resolves its target before it previews anything, so a multi-runtime host needs -r here too:

$ rbln-ctk cdi generate --dry-run
$ sudo rbln-ctk runtime configure -r docker --dry-run

Device Selection

The CDI spec exposes three forms of device handle. Pick the one that matches the workload's scope:

Handle Selects
rebellions.ai/npu=all Every NPU and every RSD group on the host (recommended). Each node keeps its host path.
rebellions.ai/npu=N A single NPU by index (0, 1, ...). The NPU's RSD group is attached automatically and appears inside the container as /dev/rsd0 whatever its host index.
rebellions.ai/npu=rsdN A single RSD group by index (rsd0, rsd1, ...), exposed inside the container as /dev/rsd0. Selects a group explicitly, bypassing the automatic NPU↔RSD mapping (debugging and custom-group workflows).

Use the =N handle to select a single NPU by index:

1
2
3
$ docker run \
  --device rebellions.ai/npu=N \
  -it IMAGE_NAME:TAG

Repeat --device to attach multiple NPUs. When the selected NPUs share an RSD group, the container can run tensor-parallel workloads across them:

1
2
3
4
$ docker run \
  --device rebellions.ai/npu=0 \
  --device rebellions.ai/npu=1 \
  -it IMAGE_NAME:TAG

NPUs outside RSD group 0 need no extra flags. After rbln-smi group -c 1 -a 4,5,6,7, the group's host node /dev/rsd1 is exposed to the container as /dev/rsd0, which is the only path the UMD opens:

1
2
3
4
5
6
$ docker run \
  --device rebellions.ai/npu=4 \
  --device rebellions.ai/npu=5 \
  --device rebellions.ai/npu=6 \
  --device rebellions.ai/npu=7 \
  -it IMAGE_NAME:TAG

One RSD group per container

Every =N and =rsdN handle renames its group device to /dev/rsd0, so a container can hold NPUs from a single RSD group only. Selecting NPUs from two groups (for example npu=0 and npu=4 after the regrouping above) makes both handles claim /dev/rsd0; the container cannot see both groups, and which node ends up there is left to the OCI runtime rather than fixed by the toolkit. Run one container per group, or use =all, which keeps every node under its host path.

Upgrading from v0.2.4 or earlier

Toolkit versions before v0.2.5 kept the host path for =N as well, so NPUs outside group 0 needed a manual --device /dev/rsdM:/dev/rsd0. That workaround is no longer necessary and can be dropped.

On Kubernetes runtimes (containerd / CRI-O), only the =all handle is published — it carries the library mounts and the rbln-smi tool bind, with no device nodes attached. Per-Pod /dev/rbln* allocation is owned by the device plugin or the DRA driver. The per-NPU handle applies to standalone Docker hosts.

Regenerate the CDI spec after RSD topology changes

rbln-ctk cdi generate binds each =N handle in the CDI spec to the /dev/rsd* nodes present on the host at generate time. The automatic refresh paths (Systemd Integration, Automatic CDI Refresh) fire only on driver or toolkit library changes. After re-grouping NPUs with rbln-smi group -c, -a, or -d, rerun sudo rbln-ctk cdi generate or wait for the next driver refresh so the container runtime can resolve the new =N handles. The =all handle remains valid across RSD group changes.

Compatibility with npu=runtime

rebellions.ai/npu=runtime from v0.1.x keeps working as an alias of =all with identical content, so existing manifests and device-plugin builds do not need to be rewritten when upgrading to v0.2. Prefer =all or a per-device selector for new manifests; the alias may be retired in a future release once downstream consumers have migrated.

RDS Device Injection

Rebel100™ only

RDS is supported on Rebel100™ only. RDS devices exist only on hosts with Rebel100™ driver v3.5 or later.

RDS (Rebellions Direct Storage) is the data path along which an RBLN NPU reads from and writes to an NVMe SSD directly. Its device /dev/rblnfs* is injected through its own rebellions.ai/rds class, independent of the NPU class. RDS is unrelated to RSD groups.

The class is written to its own spec file, /var/run/cdi/rbln-rds.yaml, and is never part of a rebellions.ai/npu=all selection. Because it reaches only containers that reference it explicitly, it does not conflict with the device plugin's NPU allocation.

Handle Selects
rebellions.ai/rds=all Every /dev/rblnfs* device on the host
rebellions.ai/rds=rblnfsN A single RDS device by name (rblnfs0, rblnfs1, ...)

On Docker, pass it alongside the NPU handle:

1
2
3
4
$ docker run \
  --device rebellions.ai/npu=all \
  --device rebellions.ai/rds=all \
  -it IMAGE_NAME:TAG

On a Kubernetes runtime with CDI enabled (containerd 1.7+, CRI-O), request it through a Pod annotation:

1
2
3
metadata:
  annotations:
    cdi.k8s.io/rblnfs: rebellions.ai/rds=rblnfs0

The RDS spec is emitted regardless of the Kubernetes device-node gate, so it behaves the same under a DaemonSet deployment. On a host with no /dev/rblnfs*, a spec file left by an earlier run is removed, so a node without RDS never advertises an empty class.

Override the spec path with rbln-ctk cdi generate --rds-output. Whichever path you choose must sit in a CDI spec directory the container runtime scans.

CLI Reference

Global Options

These apply to every rbln-ctk subcommand.

Flag Description Default
-c, --config Configuration file path (RBLN_CTK_CONFIG, then /etc/rbln/container-toolkit.yaml, then .yml)
-d, --debug Enable debug logging false
-q, --quiet Suppress non-error output false

rbln-ctk cdi generate

Discovers RBLN libraries and tools, then writes a CDI spec.

$ sudo rbln-ctk cdi generate
Flag Description Default
-o, --output NPU spec output path (- for stdout) /var/run/cdi/rbln.yaml
--rds-output RDS spec output path. Ignored with -o - or --dry-run, where the RDS spec follows the NPU spec on stdout /var/run/cdi/rbln-rds.yaml
-f, --format Output format (yaml or json) yaml
--driver-root Root path for driver files (CoreOS: /host) /
--container-library-path Isolated library path in container (same as host)
--dry-run Preview without writing false

rbln-ctk runtime configure

Enables CDI support in the container runtime config. The runtime is picked automatically only when exactly one runtime socket is present; otherwise -r is required.

$ sudo rbln-ctk runtime configure
Flag Description Default
-r, --runtime Force specific runtime (containerd, crio, docker) (auto-detect)
--config-path Custom runtime config path (runtime default)
--cdi Enable CDI support true
-y, --yes Skip the confirmation prompt false
--dry-run Preview changes false

The containerd config file is rewritten

Targeting containerd reads and rewrites config.toml through a TOML parser. Every value is preserved, but comments and key ordering are not. If config.toml is not valid TOML, the command stops with an error and leaves the file unmodified. CRI-O (drop-in file) and Docker (daemon.json) are unaffected.

rbln-ctk cdi list

Lists discovered RBLN libraries and tools.

$ rbln-ctk cdi list
Flag Description Default
-f, --format Output format (table, json, yaml) table
--driver-root Root path for driver files /

rbln-ctk info

Displays system information including detected runtime and configuration.

$ rbln-ctk info
Flag Description Default
-f, --format Output format (table, json, yaml) table

rbln-ctk version

Prints the toolkit version and build information.

$ rbln-ctk version

rbln-ctk-daemon runtime

A one-shot command that applies or reverts the runtime configuration and CDI spec files a DaemonSet left behind. Each runtime subcommand (docker, containerd, crio) carries setup and cleanup.

setup runs three steps in order: it enables CDI in the runtime config (containerd's config.toml and Docker's daemon.json are first copied to a .backup file; CRI-O gets the 99-rbln.conf drop-in), writes rbln.yaml to the CDI spec directory along with rbln-rds.yaml on hosts that have RDS devices, and restarts the runtime — by default with SIGHUP for containerd and Docker and systemctl restart for CRI-O. Unlike the DaemonSet, it does not first check whether CDI is already enabled, so every run rewrites the config and restarts the runtime.

# Enable CDI in the runtime config, write the CDI specs, and restart the runtime
$ sudo rbln-ctk-daemon runtime containerd setup

cleanup runs three steps in order: it removes rbln.yaml and rbln-rds.yaml from the CDI spec directory, reverts the runtime config (containerd and Docker are restored from their .backup file, CRI-O's 99-rbln.conf drop-in is deleted), and restarts the runtime.

# Remove the CDI specs, revert the runtime config, and restart the runtime
$ sudo rbln-ctk-daemon runtime containerd cleanup

Delete the DaemonSet before running cleanup

cleanup locks the same PID file the running daemon holds through its /run/rbln host mount, so it fails with another instance is already running while the DaemonSet is still scheduled on the node. The rbln-ctk-daemon binary is placed on the host only by the DEB / RPM package. On a node that received the toolkit solely as a container image, run the same subcommand from that image with the host root mounted and pass --host-root-mount /host; unlike the main daemon, the runtime subcommands do not detect /host on their own.

Flag Description Default
--restart-mode Runtime restart method (signal, systemd, none) (runtime default)
--host-root-mount Host root mount path for containerized deployment (empty)
--socket Runtime socket path (runtime default)
--cdi-spec-dir CDI spec output directory /var/run/cdi
--config-path Runtime config path override (runtime default)
--pid-file PID file path used for locking /run/rbln/toolkit.pid
--dry-run Preview changes false

Kubernetes Deployment

For Kubernetes clusters, deploy the toolkit as a DaemonSet. The daemon (rbln-ctk-daemon) does the following:

  1. Generates the CDI spec on startup — runtimes rescan the spec directory at container-creation time, so this never needs a restart
  2. Configures and restarts the runtime only on nodes where CDI is not yet enabled
  3. Serves health check endpoints
  4. Removes the CDI spec files it wrote on SIGTERM (pod termination)

The runtime config is not restored on shutdown

On SIGTERM the daemon does not revert the runtime config to its backup, because reverting would itself require a runtime restart. Removing the DaemonSet therefore leaves the runtime config in its post-install state. To restore it explicitly, delete the DaemonSet first and then run cleanup from rbln-ctk-daemon runtime.

Container Image

The official container image is available on Docker Hub:

$ docker pull rebellions/rbln-container-toolkit:latest

Deploy

$ kubectl apply -f deployments/kubernetes/daemonset.yaml

Health Endpoints

Endpoint Probe Type Returns 200 When
/live Liveness Daemon process is running
/ready Readiness Setup is complete
/startup Startup Initialization finished

Environment Variables

Variable Description Default
RBLN_CTK_DAEMON_RUNTIME Container runtime (auto-detect)
RBLN_CTK_DAEMON_HOST_ROOT Host root mount path / (host), /host (container)
RBLN_CTK_DAEMON_DRIVER_ROOT Driver root path for CDI spec /
RBLN_CTK_DAEMON_CDI_SPEC_DIR CDI spec directory /var/run/cdi
RBLN_CTK_DAEMON_CONFIG_PATH Runtime config path override (runtime default)
RBLN_CTK_DAEMON_CONTAINER_LIBRARY_PATH Container library path for library isolation (empty)
RBLN_CTK_DAEMON_SOCKET Runtime socket path (auto-detect)
RBLN_CTK_DAEMON_HEALTH_PORT Health check port 8080
RBLN_CTK_DAEMON_SHUTDOWN_TIMEOUT Graceful shutdown timeout 30s
RBLN_CTK_DAEMON_PID_FILE PID file path /run/rbln/toolkit.pid
RBLN_CTK_DAEMON_NO_CLEANUP_ON_EXIT Skip cleanup on exit false
RBLN_CTK_DAEMON_REFRESH_INTERVAL Poll interval for rbln-ctk-daemon's in-process CDI spec refresh (0 disables). DaemonSet-only; the DEB/RPM CLI path uses the systemd unit instead. See Automatic CDI Refresh. 60s
RBLN_CTK_DAEMON_DEBUG Enable debug logging false
RBLN_CTK_DAEMON_FORCE Terminate existing instance before starting false

RBLN_CTK_DAEMON_CONFIG_PATH must be an absolute path; the daemon refuses to start otherwise. It is treated as the final path inside the daemon filesystem and is not auto-prefixed with RBLN_CTK_DAEMON_HOST_ROOT. Callers passing host-relative paths, as on rke2 and k3s, must include the prefix themselves (for example, /var/lib/rancher/rke2/... → /host/var/lib/rancher/rke2/...).

Automatic CDI Refresh

rbln-ctk-daemon (the Kubernetes DaemonSet binary) drives the refresh loop. On DEB/RPM hosts, the systemd unit described in Systemd Integration handles the same job.

The daemon periodically fingerprints the host's RBLN UMD libraries (librbln-ccl, librbln-thunk) by their GNU build-id and regenerates /var/run/cdi/rbln.yaml whenever a fingerprint changes. Libraries with no readable build-id fall back to a size and mtime digest. Because the daemon tracks library changes automatically, operators can reinstall or upgrade the driver while keeping the DaemonSet in place. The container runtime mounts the current library into each new container at startup.

RBLN_CTK_DAEMON_REFRESH_INTERVAL (or the --refresh-interval flag) controls the refresh period. The default is 60s; setting 0 disables polling. The daemon writes the spec to a temporary file, then swaps it in with fsync followed by rename, so the container runtime always reads a complete spec.

The /ready endpoint response includes a cdi-refresh block that reports the last run time, the number of libraries discovered, and the most recent refresh error (if any).

Kubernetes Pod Example

apiVersion: v1
kind: Pod
metadata:
  name: rbln-workload
spec:
  containers:
  - name: app
    image: ubuntu:22.04
    resources:
      limits:
        rebellions.ai/npu: "1"

CoreOS / OpenShift

For Red Hat CoreOS environments where the host filesystem is mounted at /host:

1
2
3
env:
  - name: RBLN_CTK_DAEMON_HOST_ROOT
    value: "/host"

Advanced Configuration

Library Isolation

By default, RBLN libraries are bind-mounted at their host paths inside the container. If this causes conflicts (e.g., different glibc versions), use library isolation:

$ sudo rbln-ctk cdi generate --container-library-path /rbln/lib64

This mode:

  • Mounts libraries to an isolated path (/rbln/lib64) instead of host paths
  • Uses the CDI hook to run ldconfig inside the container at startup
  • Avoids LD_LIBRARY_PATH — the ldcache handles library resolution natively
  • Supports setuid binaries (which ignore LD_LIBRARY_PATH)

Systemd Integration

The DEB and RPM packages install two systemd units under /usr/lib/systemd/system/ and the postinstall script enables them automatically:

  • rbln-cdi-refresh.path — watches librbln-ml.so under the standard library paths and the rbln-ctk binary
  • rbln-cdi-refresh.service — oneshot unit that runs rbln-ctk cdi generate --output /var/run/cdi/rbln.yaml

A driver re-install or a toolkit upgrade triggers the path unit, which runs the service unit and rewrites the CDI spec on the host. Newly started containers bind to the current libraries without any operator action.

Verify the path watcher is active:

$ systemctl status rbln-cdi-refresh.path

To turn the refresher off (for example, on a host that drives CDI regeneration via a custom workflow):

$ sudo systemctl disable --now rbln-cdi-refresh.path

This systemd path applies to DEB/RPM installs only. On Kubernetes, the equivalent loop runs in-process inside rbln-ctk-daemon — see Automatic CDI Refresh.

Configuration File

The toolkit reads configuration from /etc/rbln/container-toolkit.yaml.

All CLI flags can also be set via environment variables with the prefix RBLN_CTK_ (e.g., --driver-root becomes RBLN_CTK_DRIVER_ROOT).

Key configuration sections:

Section Controls
cdi Output path, format, vendor/class names
rds RDS class name, spec output path, /dev/rblnfs* discovery patterns
libraries Discovery patterns, plugin paths, container isolation path
tools Which CLI tools to include (e.g., rbln-smi)
devices NPU/RSD device-node discovery patterns and the disabled switch. rbln-ctk-daemon turns disabled on by itself on containerd and CRI-O, so no edit is needed for Kubernetes
search-paths Where to look for libraries and binaries
glibc-exclude System libraries to exclude from CDI spec
selinux SELinux label applied to bind mounts (enable on RHEL and CoreOS)
hooks CDI hook binary and ldconfig paths

Troubleshooting

CDI spec not generated

1
2
3
4
5
6
7
8
# Verify RBLN driver is installed
$ ls /usr/lib64/librbln-*.so*

# Run with debug output
$ rbln-ctk cdi generate --debug

# Check what was discovered
$ rbln-ctk cdi list

Container cannot find RBLN libraries

1
2
3
4
5
# Verify hook is installed
$ ls -la /usr/local/bin/rbln-cdi-hook

# Regenerate CDI spec
$ sudo rbln-ctk cdi generate

Runtime not picking up changes

If the runtime is not recognizing CDI devices after configuration, try restarting it manually:

$ sudo systemctl restart containerd  # or crio, docker

Permission errors

Most operations require root access:

$ sudo rbln-ctk cdi generate
$ sudo rbln-ctk runtime configure

Next Steps

  • NPU Allocation — Learn how to allocate specific NPUs to containers using RSD groups