Nephio-Orchestrated Private 5G Stack
This guide describes how a complete private 5G network, an SD-Core control plane, a BESS user plane and an OCUDU-RAN gNB, is deployed onto bare-metal edge servers from a single declarative intent object, with no imperative per-site scripting.
A management cluster runs Nephio; an operator turns a short EdgeSite or Fleet intent into Nephio PackageVariants; Porch renders them, Config Sync delivers them, and Cluster API provisions a fresh Kubernetes cluster on each target server. The addressing, the network slice, the subscriber records and the N2/N3/N4/N6 datapath are all derived from the intent rather than hand-configured. The result is a reproducible, one-command-per-site deployment where adding, changing or removing a 5G edge is a change to a single Kubernetes object.
The end-to-end control flow. Everything below the intent is Kubernetes-native and continuously reconciled; there is no SSH-driven configuration on the data path.
Nephio treats a 5G network the way Kubernetes treats an application: as desired state expressed in declarative objects, reconciled by controllers, and version-controlled in Git. This document focuses on the automation, how one intent object becomes a running multi-cluster 5G deployment, rather than on the internals of the individual network functions, which are covered in their own pages.
Overview
Standing up a private 5G network by hand is a sequence of tightly-coupled, error-prone steps: prepare a host, install Kubernetes, lay down a CNI, deploy a control plane, wire a user plane, attach a radio, allocate IP ranges that do not collide, provision subscribers, and stitch the N2/N3/N4/N6 reference points across the whole thing. Every one of those steps is a place to drift. Repeat it across several edge sites and the configuration becomes impossible to reason about, and impossible to reproduce.
Nephio is a Kubernetes-based automation platform, built in the Linux Foundation, for exactly this problem: deploying and managing cloud-native network functions across large numbers of clusters using intent and GitOps rather than imperative tooling. Its model is that the operator declares what they want, a 5G edge on this server with this radio, and the platform continuously reconciles the world to match, generating the per-cluster configuration automatically and delivering it through Git.
This integration applies that model to a specific, validated stack:
- The control plane is SD-Core. AMF, SMF, NRF, AUSF, NSSF, PCF, UDM, UDR, a web console and a MongoDB backing store, deployed as a Nephio package.
- The user plane is a BESS UPF in
af_packetmode, deployed as its own package and wired to the control plane over N4. - The radio is an OCUDU-RAN gNB, an integrated CU/DU, deployed as a package and auto-wired to the site's AMF, PLMN, TAC and slice.
- The glue is a purpose-built operator that expresses all of the above as a single
EdgeSiteorFleetintent and expands it into Nephio packages.
The practical outcome is that a new 5G edge, a full self-contained core plus radio, or a lightweight user-plane-only site steered by a central control plane, is created, changed or torn down by editing one Kubernetes object.
Why a Helm chart is not enough
The obvious way to package a 5G core is a Helm chart, applied per cluster. That works for a single, hand-built cluster, but it does not address the parts that actually make multi-site 5G hard:
- It assumes the cluster already exists. A Helm chart cannot turn a bare server into a Kubernetes cluster. Something still has to provision the node, the CNI and the runtime before any chart runs.
- It does not allocate addressing. Every site needs a non-overlapping UE pool and distinct N3/N4/N6 subnets. A static chart either hard-codes these, which does not scale past one site, or pushes the problem back onto the operator.
- It does not specialise per site. The gNB must be told this site's AMF address, PLMN, TAC and slice; the UPF must be told this site's UE pool. That specialisation is per-cluster and derived, not templated once.
- It is not continuously reconciled across clusters. A chart is applied and forgotten. Drift on a remote edge is invisible until something breaks.
Nephio closes these gaps by treating the whole deployment, cluster included, as reconciled intent. Package specialisation (rendering per-site values into each package) and package delivery (GitOps reconciliation onto each cluster) are first-class, and cluster provisioning is driven by the same declarative loop. The Helm charts do not disappear; they become blueprints that Nephio renders and places, rather than artefacts an operator applies by hand.
Concepts
A handful of Nephio and Kubernetes building blocks carry the whole design. Each is standard; the integration is in how they are composed.
| Building block | Role in this design |
|---|---|
| KRM and CRDs | Everything is a Kubernetes Resource Model object. The intent is expressed with two CRDs, EdgeSite and Fleet, under the sdcore.nephio.io API group. Declaring intent is kubectl apply of a small YAML document. |
| The operator pattern | A controller watches those CRDs and drives the world toward the declared state. A single Go operator hosts several controllers, one per concern (site expansion, fleet fan-out, cross-cluster mesh, datapath), each reconciling continuously. |
| Porch and PackageVariants | Porch is a Kubernetes extension API server managing the lifecycle of kpt configuration packages. A PackageVariant is intent to take an upstream blueprint and render a specialised copy with per-site values injected. The operator emits PackageVariants; Porch produces the concrete package revisions. |
| Blueprints | Upstream, reusable packages, one each for the SD-Core control plane, the BESS UPF, the OCUDU gNB, Multus and the datapath actuator, held in a catalog. A blueprint is specialised, not edited, so moving to a newer network-function release is a change to the blueprint rather than to the operator. |
| Config Sync and GitOps | Config Sync continuously reconciles a cluster to the contents of a Git repository. Published packages land in an in-cluster Gitea repository; each edge cluster's Config Sync pulls its own packages and keeps applying them, so drift self-heals. |
| Cluster API and BYOH | Cluster API is the Kubernetes-standard way to provision clusters declaratively. The Bring-Your-Own-Host provider lets it provision onto an already-running bare-metal or VM host that has registered an agent. The bootstrap target is Canonical Kubernetes, with Cilium as the CNI (SCTP enabled for N2, ClusterMesh available for cross-cluster reference points). |
| IPAM | A Nephio-managed address pool hands out non-overlapping ranges, the UE pool and the N3/N4/N6 subnets, as IP claims, so no two sites collide and no addressing is hand-assigned. |
Architecture
The deployment has two tiers: one management cluster that runs Nephio and the operator, and any number of edge clusters, one per bare-metal server, that run the actual 5G network functions.
The intent API: EdgeSite and Fleet
An EdgeSite describes a single site. It is deliberately small, because the operator derives everything else.
apiVersion: sdcore.nephio.io/v1alpha1
kind: EdgeSite
metadata:
name: edge-01
spec:
server: 192.168.1.111 # a registered bare-metal host
role: cp+upf # self-contained core + UPF (or: upf-only)
ran:
device: n310 # OCUDU gNB radio: n310 | b210 | zmq
role: cp+upf yields a self-contained site: an SD-Core control plane, a BESS UPF and, because ran is present, an OCUDU gNB, all on the one cluster, with N2 and N4 kept local. role: upf-only yields a lightweight site whose UPF is steered by a control plane on another cluster, with N2/N4 carried over Cilium ClusterMesh. The ran.device selects the radio the OCUDU gNB drives: a networked USRP N310, a USB b210, or the ZMQ RF simulator for hardware-free validation.
A Fleet is a list of sites, reconciled as a unit:
apiVersion: sdcore.nephio.io/v1alpha1
kind: Fleet
metadata:
name: fleet
spec:
sites:
- server: 192.168.1.111
role: cp+upf
ran: { device: n310 }
- server: 192.168.1.112
role: upf-only
Adding, changing or removing a site is an edit to this one object. Each entry is expanded into an owned EdgeSite; removing an entry garbage-collects the site.
The management cluster
The management cluster is bootstrapped once. It runs the Kubernetes API server, the operator, Porch, the in-cluster Gitea, the Config Sync source, the IPAM pool, and the Cluster API stack with the BYOH provider and the Canonical Kubernetes bootstrap provider. Nothing about a 5G network function lives here: the management cluster's job is to produce and place configuration, not to run the network.
Nephio itself, in C4 form: the actors, the containers inside the system boundary, the Porch component view and the Nephio Core APIs and controllers.
Package generation and delivery
When an EdgeSite is applied, the operator reconciles it into a set of Nephio packages:
- It claims addressing from IPAM, a UE pool and N3/N4/N6 subnets, so the site's ranges are unique by construction.
- It emits one
PackageVariantper network function: the SD-Core control plane, the BESS UPF, the OCUDU gNB (whenranis set), Multus with the site's network-attachment definitions, and the datapath actuator. - Porch clones the matching blueprint from the catalog and renders each variant with the site's derived values: AMF address, PLMN, TAC, slice identifiers, and the claimed subnets.
- An auto-approver promotes each rendered package revision from Draft to Published.
- The published packages land in Gitea, the deployment repository that is the single source of truth for what each site should run.
Nephio Core in detail: the five CRD groups, the six reconciler controllers, and what each one actuates.
Cluster provisioning
In parallel with package generation, the operator declares the infrastructure the packages will run on. It creates a ByoHost for the registered server and a Cluster object; Cluster API with the BYOH provider bootstraps Canonical Kubernetes on that host, with Cilium as the CNI. This is the step a Helm chart cannot do: it turns a bare server into a Kubernetes cluster, declaratively, from the same reconcile loop that produced the packages.
Once the new cluster is up, its Config Sync agent registers against the Gitea repository and pulls the packages destined for that site. From here the edge cluster is self-driving: Config Sync applies the SD-Core control plane, the BESS UPF, the OCUDU gNB and Multus, and keeps them reconciled.
The edge site
Each edge cluster runs the 5G network itself: the SD-Core control plane (AMF, SMF, NRF, AUSF, NSSF, PCF, UDM, UDR, web console, MongoDB), the BESS UPF in af_packet mode, and, for a cp+upf site with a radio, the OCUDU-RAN gNB, an integrated CU/DU that drives the selected USRP and terminates N2 at the AMF and N3 at the UPF. Multus attaches the N3 and radio interfaces to the pods that need them.
The datapath actuator
The one part of a running 5G network that lives below Kubernetes is the host packet path: the routes, the N6 egress NAT, and the Layer-2 next-hop the UPF stamps onto downlink traffic. These cannot be expressed as ordinary pod manifests, so a small datapath actuator runs as a DaemonSet on each edge node. It watches a UPFDataPath object the operator emits and programs the host to match, installing the downlink route to the gNB, the N6 egress NAT out the real uplink NIC, and the correct next-hop MAC, so the automation extends all the way to the wire rather than stopping at the pod boundary.
How it works
End to end, a single kubectl apply becomes a running 5G edge through a continuous reconcile loop:
- Declare. The operator applies an
EdgeSite, or edits aFleet, on the management cluster. - Reconcile intent. The operator controller picks up the object, claims addressing from IPAM, and computes the site's derived values: AMF address, PLMN, TAC, slice, subnets.
- Generate packages. It emits one
PackageVariantper network function; Porch clones each blueprint and renders it with those values. - Publish. The auto-approver promotes the rendered revisions to Published; they land in Gitea.
- Provision. In parallel, CAPI with the BYOH provider bootstraps Canonical Kubernetes on the target server.
- Deliver. The new cluster's Config Sync pulls its packages and applies the SD-Core control plane, BESS UPF, OCUDU gNB and Multus.
- Wire the datapath. The datapath actuator programs the host routes, N6 NAT and downlink next-hop so the N3/N6 user plane forwards.
- Serve. The OCUDU gNB drives its radio; a UE attaches to the SD-Core control plane, establishes a PDU session, and user traffic egresses via the UPF's N6.
Because every step is a reconciler, the loop is self-healing: if a package drifts, Config Sync re-applies it; if a host is re-onboarded, provisioning starts from a clean state. And because the intent is a single object, teardown is symmetric. Deleting the EdgeSite, or removing a Fleet entry, garbage-collects the packages, Config Sync prunes the workloads, and CAPI deprovisions the cluster. There is no per-host teardown script.
What this delivers
- One-object-per-site 5G. A complete edge, core, user plane and radio, from a single
EdgeSite, or a whole estate from a singleFleet. - Automatic specialisation. UE pool, N3/N4/N6 subnets, AMF address, PLMN, TAC and slice are derived from intent and IPAM, not hand-assigned.
- Declarative cluster provisioning. Bare servers become Kubernetes clusters through CAPI/BYOH, inside the same reconcile loop that produces the workloads.
- GitOps delivery and self-healing. Every site's configuration is version-controlled in Git and continuously reconciled by Config Sync.
- Two topologies from one API.
cp+upfself-contained cores andupf-onlyuser-plane edges, steered over ClusterMesh, share the same intent surface. - Datapath automation to the wire. The host N3/N6 path is programmed by a controller, not by SSH.
- Symmetric teardown. Deleting intent releases the cluster; no imperative decommissioning.
Why this matters
The value is in the separation of concerns. Intent is authored once, in one small object, by whoever owns the service. Specialisation, turning that intent into per-site configuration, is done by controllers, deterministically, so two sites never drift apart by accident. Delivery is GitOps, so the source of truth is a repository rather than the live state of a remote cluster. Provisioning is declarative, so the cluster is as reproducible as the workloads on it.
This is the same architectural discipline Kubernetes brought to applications, applied to a 5G network: desired state, reconciled continuously, version-controlled, and composed from reusable blueprints. A network function release is a blueprint bump. A new site is a line of YAML. An estate is a Fleet.
What it is, and what it is not
What it is. A reproducible, intent-driven method for deploying SD-Core, a BESS UPF and an OCUDU-RAN gNB across one or many bare-metal edge sites, using Nephio for specialisation and delivery and Cluster API for provisioning. It automates the full path from a single intent object to a running, self-healing 5G edge, including the host datapath.
What it is not. It is not a fork of Nephio, SD-Core or OCUDU-RAN: the network functions are deployed as blueprints and specialised, not modified. It is not a claim of conformance certification; it is a deployment and lifecycle-automation method, and the network functions carry their own conformance posture. It does not replace an SMO or a service orchestrator above it; it presents a Kubernetes-native intent surface that such a system can drive.
Roadmap
- Broader radio and split support. Additional OCUDU-RAN configurations, including Open Fronthaul (7.2) to external O-RUs alongside the integrated-SDR (split 8) path.
- Richer topology intent. Declarative expression of multi-site meshes and control-plane sharing beyond the current
cp+upfandupf-onlysplit. - Blueprint currency. Continuous tracking of upstream SD-Core and OCUDU-RAN releases as blueprint revisions, so a version bump is a package change.
- Observability as intent. Per-site metrics and datapath health surfaced through the same reconciled model.
Deployment
A complete edge is a single command chain. On the management server:
# 1. Clone
git clone https://github.com/TOSSI-Foundation/Nephio-Stack.git && cd Nephio-Stack
# 2. Bootstrap the management plane (once)
make prereqs # pinned toolchain (go, kustomize, clusterctl, kpt)
make mgmt # Canonical K8s + CAPI + BYOH + Nephio (Porch) + in-cluster Gitea
make publish # operator + blueprints + auto-approver + IPAM pool
# 3. Build the OCUDU gNB image (any radio site needs it)
make gnb-image OCUDU_SRC=<path-to-ocudu-clone>
Declare a site in fleet.yaml, an SD-Core control plane with a BESS UPF and an OCUDU gNB:
apiVersion: sdcore.nephio.io/v1alpha1
kind: Fleet
metadata: { name: fleet, namespace: default }
spec:
sites:
- server: <edge-ip>
user: <login>
role: cp+upf
ran:
split: "8"
device: n310 # n310 | b210 | x310 | zmq (RF simulator)
Reconcile the whole fleet. This provisions the cluster and deploys the stack:
make sites
kubectl get edgesites,clusters # wait until the site's cluster is Ready
Tear the whole fabric down. This is declarative too: CAPI deprovisions the clusters and the hosts are wiped pristine.
make down
Step-by-step guides, for RF-sim at 50-UE scale and for split-8 with a real SDR, are in the TI repository.
Validation
This stack has been validated end to end: an SD-Core control plane, a BESS edge UPF and an OCUDU-RAN gNB, all deployed from a single intent, serving 50 simulated UEs, which attached, established PDU sessions and passed traffic through the Nephio-deployed core and UPF.
Conclusion
A private 5G network does not have to be assembled by hand. Expressed as intent and reconciled by controllers, an entire edge, control plane, user plane and radio, becomes a single Kubernetes object, and an estate becomes a list of them. The platform handles the specialisation, the provisioning, the delivery and the datapath; the operator handles the intent.
Resources
- Source code: TOSSI-Foundation/Nephio-Stack
- Deployment guides: TOSSI-Foundation/TI, under
nephio/
Related
The network functions deployed here are documented separately: SD-Core with eUPF covers the XDP datapath and usage metering, and the rfsimulator multi-UE testbed covers hardware-free validation with the ZMQ and rfsimulator radio paths. For certification of the deployed functions, see Cloud Native Telecom Certification.