Docker Deployment
The RIC, the E2 emulator, the SDK base, and every xApp ship as Docker images built by a single script. A Docker Compose stack brings up a complete demo topology.
Building images
From the project root:
./build_docker.sh # build everything: RIC + SDK base + emulator + all xApps
./build_docker.sh ric # RIC server only → airpuls-ric:latest
./build_docker.sh emulator # E2 node emulator only → airpuls-e2-emulator:latest
./build_docker.sh base # xApp SDK base only → airpuls-xapp-sdk:latest (+ airpuls/airpuls-xapp-sdk:latest)
./build_docker.sh kpm-monitor # all languages of one xApp
./build_docker.sh kpm-monitor:go # one xApp / language combo
xApp images are tagged <name>-<lang>:latest (e.g.
kpm-monitor-python:latest). See SDK → Packaging
for how xApp images are structured.
Running the RIC server alone
A licence is required
The RIC refuses to start without a valid licence for the deployment, so
the commands below need one mounted. See
Licensing for how to obtain it; a deployment managed by
nrtric-ctrl already has one at conf/license.key, bound to the CA in
conf/ric-tls/ca.crt.
docker run --rm \
-v $(pwd)/conf/license.key:/etc/airpuls/license.key:ro \
-v $(pwd)/conf/ric-tls/ca.crt:/etc/airpuls/tls/ca.crt:ro \
-p 36421:36421/sctp \
-p 36422:36422 \
-p 9095:9095 \
airpuls-ric:latest
The baked configuration reads the licence from /etc/airpuls/license.key and
identifies the deployment by the certificate at /etc/airpuls/tls/ca.crt, so
both mounts are needed. Without them the container exits reporting which one
is missing.
Override the baked config by mounting your own:
docker run --rm \
-v $(pwd)/nrtric.yml:/etc/airpuls/nrtric.yml:ro \
-p 36421:36421/sctp -p 36422:36422 -p 9095:9095 \
airpuls-ric:latest
Environment variables
The entrypoint (docker-entrypoint.sh) substitutes a small set of env
vars into the baked nrtric.yml before exec'ing the server — the
least-invasive way to adjust a container without a config mount:
| Variable | Values | Default | Meaning |
|---|---|---|---|
RIC_BIND_ADDR |
IPv4 literal | auto-detect | SCTP bind address. Explicit is mandatory for --network host |
RIC_BIND_INTERFACE |
interface name | eth0 |
Interface to auto-discover the first IPv4 on, when RIC_BIND_ADDR is unset |
Don't bind SCTP to 0.0.0.0 in a container
SCTP is multihomed — binding to 0.0.0.0 inside a container
advertises every bridge IP to the peer, and if the peer picks an
unreachable one the association drops silently. The entrypoint
auto-discovers the container's eth0 IP by default; set
RIC_BIND_ADDR explicitly for --network host:
Full-stack Compose demo
docker/compose/docker-compose.yml brings up the RIC together with an E2
emulator and a KPM monitor on a private bridge:
| Service | Image | Role |
|---|---|---|
airpuls-ric |
airpuls-ric:latest |
Near-RT RIC server |
emu-gnb |
airpuls-e2-emulator:latest |
E2 node emulator (SCTP peer) |
kpm-monitor |
kpm-monitor:latest |
KPM monitoring xApp |
Startup order is enforced by health checks: the RIC comes up first and waits until its metrics endpoint answers, the emulator then connects over SCTP, and the xApp connects over the northbound IPC and subscribes.
docker compose logs -f # all services
docker compose logs -f kpm-monitor # the xApp only
docker compose restart kpm-monitor # after editing docker/compose/xapp.yml
docker compose down # teardown
Build the emulator image
The emu-gnb service uses airpuls-e2-emulator:latest — build it
with ./build_docker.sh emulator. Its advertised SMs and RIC target
come from the bind-mounted docker/compose/emulator.yml.
Running a standalone xApp
docker run --rm --network=host \
-v $(pwd)/xapp.yml:/etc/airpuls/xapp.yml \
kpm-monitor-python:latest -c /etc/airpuls/xapp.yml
See SDK → Packaging for building your own xApp image and xApps for the reference apps.