Reusable container images for the next-nf org, built from source and published to the GitHub Container Registry (GHCR).
These images are dependencies for the udr
project's interop demos. They change rarely, so we build them once, host them in
GHCR, and let the demos pull prebuilt images instead of compiling from source on
every run.
The first (and currently only) image is srsran-4g. More may be added as
needed.
Everything is built with podman/buildah (no Docker), including in CI.
Each image type lives in its own directory holding its Containerfile and any
associated files (configs, scripts, …):
support-containers/
├── srsran-4g/
│ ├── Containerfile # multi-stage podman build
│ └── .containerignore
├── .github/workflows/
│ └── build-and-push.yml # podman multi-arch build → GHCR
├── README.md
├── LICENSE # AGPL-3.0 (governs the bundled srsRAN binaries)
└── NOTICE # attribution + AGPL §13 source availability
A multi-stage build of the srsRAN 4G LTE stack with the ZeroMQ virtual-radio RF driver enabled, so the eNB and UE exchange IQ samples over TCP sockets — no radio hardware, UHD, or SoapySDR required. srsRAN also tolerates running without real-time scheduling priority, which is why it (rather than OAI) is the clean dependency for CI and cloud VMs.
| Image ref | ghcr.io/next-nf/srsran-4g |
| Tags | release_25_10, latest, sha-<commit> |
| Platforms | linux/amd64, linux/arm64 |
| srsRAN_4G version | release_25_10 (latest stable, published 2026-01-26) |
| srsRAN_4G commit | 6bcbd9e5bf8686aa7085202cd847c5ddd64a9c16 |
| Base image | ubuntu:22.04 (pinned by digest sha256:4f838adc…) |
| License | AGPL-3.0-or-later (see LICENSE / NOTICE) |
Pull:
podman pull ghcr.io/next-nf/srsran-4g:release_25_10
# or pin exactly:
podman pull ghcr.io/next-nf/srsran-4g:release_25_10@sha256:<digest>arm64 is a first-class target: the demos may run on an Oracle Cloud Ampere/ARM VM. Each arch is built on a native GitHub runner (no QEMU).
Binaries (in PATH, /usr/local/bin/):
| Binary | Role |
|---|---|
srsenb |
LTE eNodeB |
srsue |
LTE UE (with soft-USIM) |
srsepc |
Minimal LTE EPC (MME/HSS/S-GW) — usually unused in udr demos, which point srsenb at an Open5GS MME instead |
Example config files (/usr/local/share/srsran/, also exposed as
$SRSRAN_CONFIG_DIR):
| File | For |
|---|---|
enb.conf.example |
srsenb main config |
rr.conf.example |
srsenb radio resources |
rb.conf.example |
srsenb radio bearers |
sib.conf.example |
srsenb SIB / broadcast |
ue.conf.example |
srsue main config |
epc.conf.example |
srsepc config |
The udr demos mount customized copies of these over the container's config
path, so the .example files must be present as a starting point. Strip the
.example suffix (or pass an explicit path) when running.
The eNB and UE connect over ZeroMQ TCP. The RF args are passed via
--rf.device_name=zmq and a --rf.device_args string that wires each side's TX
socket to the other's RX socket. A typical pairing (eNB on :2000/:2001, UE the
mirror image):
# 1) eNB — TX on tcp://*:2000, RX on tcp://localhost:2001 (talks S1AP to an MME)
srsenb /conf/enb.conf \
--rf.device_name=zmq \
--rf.device_args="fail_on_disconnect=true,tx_port=tcp://*:2000,rx_port=tcp://localhost:2001,id=enb,base_srate=23.04e6"
# 2) UE — mirror of the eNB ports; creates a tun_srsue netdev for user-plane IP
srsue /conf/ue.conf \
--rf.device_name=zmq \
--rf.device_args="tx_port=tcp://*:2001,rx_port=tcp://localhost:2000,id=ue,base_srate=23.04e6"base_srate=23.04e6 matches a 10 MHz / 50-PRB cell; use 11.52e6 for 5 MHz /
25 PRB, etc. Both sides must agree.
Container caveats:
srsuecreates atun_srsuenetwork device for the UE's user-plane IP, which needs--cap-add=NET_ADMIN(and--device=/dev/net/tun).- Run eNB and UE on the same Docker/Podman network so the ZMQ
tx/rx_porthostnames resolve (replacelocalhostwith the peer's service name in compose), or share a network namespace. - No real-time priority is required, but you may pass
--general.rf_eal_args/ scheduling flags if available.
The udr interop demo composes three pieces to drive a real LTE attach with no radios:
srsue ──ZMQ IQ── srsenb ──S1AP/SCTP── Open5GS MME ──S6a Diameter── udr HSS
The demo's compose/manifest pulls ghcr.io/next-nf/srsran-4g:release_25_10,
runs one container as srsenb and one as srsue (commands above) with the
demo's customized enb.conf/ue.conf mounted over /usr/local/share/srsran,
and points srsenb at the Open5GS MME. The UE attach makes the MME emit S6a
AIR/ULR to the udr HSS — which is the behaviour the demo verifies. Running
the full attach is the udr repo's job, not this one; this repo only produces
the pinned image.
podman build -f srsran-4g/Containerfile -t srsran-4g:local srsran-4g
podman run --rm srsran-4g:local # prints usage + proves binaries link
podman run --rm srsran-4g:local srsenb --version
podman run --rm srsran-4g:local srsue --version
podman run --rm srsran-4g:local ls /usr/local/share/srsranThe Containerfile asserts the cloned tag's HEAD equals the pinned commit, so the
build fails loudly if upstream ever re-points release_25_10.
.github/workflows/build-and-push.yml builds and pushes
ghcr.io/next-nf/srsran-4g using podman (no Docker):
- Triggers:
workflow_dispatch(manual) andpushtomainthat touchessrsran-4g/**or the workflow. No schedule — the image is pinned and rarely changes. - Multi-arch (native): a matrix builds
linux/amd64onubuntu-24.04andlinux/arm64onubuntu-24.04-arm(native ARM runner, no QEMU) withpodman build. Each job smoke-tests its image and exports it as an OCI-archive artifact — nothing is pushed yet, so no throwaway per-arch tags pollute GHCR. Amergejob downloads both archives, assembles a single multi-arch manifest withpodman manifest create/add, and pushes it withpodman manifest push. - Tags:
release_25_10,latest, andsha-<full-commit-sha>of this repo. - Permissions:
contents: read,packages: write; logs in toghcr.ioviapodman loginwith the built-inGITHUB_TOKEN. All GitHub Actions are pinned to commit SHAs (matching the udr repo's hardening style).
The first publish creates a private package. The demos/CI must pull without auth, so make it public once. GHCR package visibility has no REST/CLI API — it must be changed in the web UI by an org owner / package admin:
GitHub →
next-nforg → Packages →srsran-4g→ Package settings → Danger Zone → Change visibility → Public.
The image's org.opencontainers.image.source label links the package to this
repo automatically. After flipping it, confirm anonymous pull works:
podman logout ghcr.io
podman pull ghcr.io/next-nf/srsran-4g:release_25_10 # should succeed without authThe packaging in this repo supports redistribution of srsRAN 4G, which is AGPL-3.0-or-later. The full license text is in LICENSE and the attribution / source-availability note is in NOTICE. The exact srsRAN source built into the image is the pinned tag/commit above.