Skip to content

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 Nephio 5G edge automation and deployment flow in three tiers. Tier one, intent, authored once and applied by one command: a platform operator applies an EdgeSite or Fleet custom resource carrying server, role (cp+upf or upf-only), ran device and subscribers. Tier two, the management cluster running the Nephio control plane, bootstrapped once: the Kubernetes API server stores the custom resources and is watched by an SD-Core operator written in Go, which reconciles EdgeSite, Fleet, MeshLink, Network and UPFDataPath controllers. The operator claims IPClaims from an IPAM called sdcore-vpc that allocates uePool, N3, N4 and N6 CIDRs; creates PackageVariants that Porch clones from a blueprint catalog holding sdcore-cp, sdcore-upf, ocudu-gnb, multus and datapath-actuator packages; passes draft PackageRevisions to an auto-approver that promotes them from Draft through Proposed to Published into Gitea, an in-cluster Git deployment repository that is the source of truth; and creates ByoHost and Cluster resources for Cluster API with the BYOH provider, which onboards a bare server and bootstraps Canonical Kubernetes with Cilium CNI, SCTP for N2 and ClusterMesh. Tier three, the edge site on a bare server running Canonical Kubernetes, repeated N times: Config Sync as a RootSync GitOps agent pulls the deploy repo and applies the SD-Core control plane (AMF, SMF, NRF, AUSF, NSSF, PCF, UDM, UDR, WebUI and Mongo), the OCUDU gNB combining CU and DU with RRC, NGAP, GTP-U, scheduler and RF, Multus with network attachment definitions attaching n3, core and radio0 to pods, and the BESS UPF in af_packet mode doing GTP-U encapsulation and decapsulation with PDR and FAR across N3, N4 and N6. A datapath actuator DaemonSet on each node programs host routes, N6 NAT and the downlink next-hop MAC.

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_packet mode, 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 EdgeSite or Fleet intent 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 blockRole in this design
KRM and CRDsEverything 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 patternA 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 PackageVariantsPorch 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.
BlueprintsUpstream, 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 GitOpsConfig 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 BYOHCluster 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).
IPAMA 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.

An intent-driven 5G stack with Nephio, presented as a C4-style architecture view. On the left, actors as each view names them: personas including a designer who creates the network function packages and a network engineer who implements and instantiates them; a person, the administrator of networks, cloud infrastructure or both; Porch view actors comprising a network administrator and external automation systems; service orchestration covering a core service orchestrator and an O-RAN service orchestrator; and an external system for GitOps and CI/CD. Edge labels describe the relationships: create and generate network topology, Kubernetes cluster and network function intent; manage the lifecycle of Nephio blueprints and packages; reconcile, integrate or deploy external artefacts; and manage package variants and package variant sets. On the right, the Nephio system container view shows five containers inside the boundary and two external ones it depends on: a Backstage-based Web UI, the Kubernetes API server, Porch as kpt-as-a-service managing the lifecycle of KRM config packages, Nephio Core as Kubernetes operators for workload and cluster specialisation and actuation, network function controllers as Kubernetes operators handling network function lifecycle for OAI Core, RAN and free5GC, plus an external reconciliation engine as the GitOps engine and an external Git repository manager. A Porch component view shows two custom resource definitions, PackageVariantSet and PackageVariant, living in the Kubernetes API server, with their two matching controllers living in Porch and reconciling package variants against what is in Git. A Nephio Core component view lists five custom resource definition APIs, Resources, Inventory, Infrastructure, Config and Workload, alongside six Kubernetes reconciler controllers, Resource Backend, Token, K8s Cluster, Repository, Network and Bootstrap Package, each drawing an arrow to the API it manages, with an inventory datastore beneath them. Controller actuation targets are the Git repository manager, supported network functions, cloud providers and the network fabric. At the bottom, an infrastructure boundary contains cloud providers such as GCP, OpenShift and local kind clusters, and network fabric elements such as Nokia SR Linux.

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:

  1. It claims addressing from IPAM, a UE pool and N3/N4/N6 subnets, so the site's ranges are unique by construction.
  2. It emits one PackageVariant per network function: the SD-Core control plane, the BESS UPF, the OCUDU gNB (when ran is set), Multus with the site's network-attachment definitions, and the datapath actuator.
  3. 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.
  4. An auto-approver promotes each rendered package revision from Draft to Published.
  5. The published packages land in Gitea, the deployment repository that is the single source of truth for what each site should run.
A detailed component diagram of the Nephio system. Inside the Nephio Core container, a Nephio Core APIs container holds five custom resource definition components: Resources, which defines IPClaim, IPPrefix, NetworkInstances, VlanClaims, VlanIndices and Vlans; Inventory, which defines endpoints, links, nodes and targets; Infrastructure, which defines workload cluster, tokens, repositories, networks and network configs; Config, which defines networks configuration; and Workload, which defines NFConfigs, NFDeployments and the AMF, UPF and SMF network functions. Below, a Nephio Core Controllers container holds six Kubernetes reconciler components: Resource Backend, which manages resource assignment and creates and manages IP and VLAN index, claim and assignment; Token, for handling security tokens used to authenticate against systems; K8S Cluster, responsible for reconciling cluster definitions with running clusters; Repository, for reconciling repository definitions; Network, responsible for reconciling network attachment definitions; and Bootstrap Package, for bootstrapping onboarded packages and deploying initial kpt packages upon cluster creation. An inventory datastore container sits beneath them. To the left, an external Git repository manager container hosts Git repositories and supports remote branch management and merging. To the right, the Kubernetes API server exposes the custom resource definitions, and network function controllers manage the lifecycle of network functions, currently supporting OAI Core, RAN and free5GC. At the lower right, an infrastructure boundary contains network fabric elements such as Nokia SR Linux and cloud providers such as GCP, OpenShift and local kind clusters.

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:

  1. Declare. The operator applies an EdgeSite, or edits a Fleet, on the management cluster.
  2. 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.
  3. Generate packages. It emits one PackageVariant per network function; Porch clones each blueprint and renders it with those values.
  4. Publish. The auto-approver promotes the rendered revisions to Published; they land in Gitea.
  5. Provision. In parallel, CAPI with the BYOH provider bootstraps Canonical Kubernetes on the target server.
  6. Deliver. The new cluster's Config Sync pulls its packages and applies the SD-Core control plane, BESS UPF, OCUDU gNB and Multus.
  7. Wire the datapath. The datapath actuator programs the host routes, N6 NAT and downlink next-hop so the N3/N6 user plane forwards.
  8. 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 single Fleet.
  • 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+upf self-contained cores and upf-only user-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+upf and upf-only split.
  • 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

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.