# Deploying free5GC on Kubernetes: A Hands-On Guide
free5GC is the most mature open-source 5GC implementation tracked against 3GPP Release 15/16, with active Release 17 work. If you want to run a 5G core for lab work, CI testing, or research, deploying it on Kubernetes is the path of least pain. The free5GC team maintains a Helm chart, the network functions are containerized, and you can iterate without rebuilding VMs.
This is a working deployment guide. I assume you know kubectl, can read Helm values, and understand what an SBI is.
Prerequisites
Your cluster needs more than a vanilla install. free5GC's UPF uses GTP-U, which requires the gtp5g kernel module on the node where the UPF runs. Without it, no PDU sessions establish.
Minimum viable setup:
- A Kubernetes cluster with at least one node running Ubuntu 22.04 LTS, kernel 5.15 or newer.
- 8 vCPU, 16 GB RAM as a baseline. NF pods are not heavy individually, but you run AMF, SMF, UPF, NRF, AUSF, UDM, UDR, PCF, NSSF, and the WebUI simultaneously.
- MongoDB. The Helm chart bundles a single-replica MongoDB deployment by default. For anything beyond a smoke test, point the chart at an external MongoDB with persistent storage.
- A CNI that supports
multus. The UPF needs an extra interface for N3 (gNB-facing GTP-U) and N6 (data network). Calico plus Multus works. - The
gtp5gmodule installed on the UPF node:
git clone https://github.com/free5gc/gtp5g.git
cd gtp5g && make && sudo make install
sudo modprobe gtp5g
lsmod | grep gtp5g
If modprobe fails, you are likely on a kernel older than 5.4 or missing headers. Fix that first.
Installing the Helm Chart
The community-maintained chart lives at github.com/Orange-OpenSource/towards5gs-helm. It tracks free5GC releases reasonably well.
helm repo add towards5gs https://raw.githubusercontent.com/Orange-OpenSource/towards5gs-helm/main/repo/
helm repo update
helm pull towards5gs/free5gc --untar
Open free5gc/values.yaml and edit the following before installing:
global.n2network.subnetIP— the subnet your gNB will reach the AMF on (N2/NGAP).global.n3network.subnetIP— UPF GTP-U subnet.global.n4network.subnetIP— SMF-UPF PFCP control.global.n6network.subnetIP— UPF to data network.free5gc-upf.upf.n6cfg.dnnList[0].dnn— your DNN, typicallyinternet.- The Multus network attachment definitions matching your cluster's network setup.
Install NRF first if you are deploying piecemeal — every other NF discovers peers through it:
helm install free5gc ./free5gc -n free5gc --create-namespace
The chart deploys all NFs in dependency order using init containers that wait on the NRF service. Watch for pods stuck in Init:
kubectl get pods -n free5gc -w
Provisioning a Subscriber
The WebUI is exposed as a NodePort or you can port-forward:
kubectl port-forward -n free5gc svc/webui-service 5000:5000
Log in (admin / free5gc by default), create a subscriber with an IMSI matching your UE simulator's config, set Ki and OPc, and assign the DNN.
Connecting UERANSIM
UERANSIM simulates a gNB and one or more UEs. Run it outside the cluster on a host that can reach the AMF's N2 service IP and the UPF's N3 IP.
In gnb.yaml:
mcc: '208'
mnc: '93'
nci: '0x000000010'
idLength: 32
tac: 1
linkIp: 127.0.0.1
ngapIp: <gnb host IP reachable to AMF>
gtpIp: <gnb host IP reachable to UPF>
amfConfigs:
- address: <AMF N2 service IP>
port: 38412
Start the gNB, then the UE. A successful registration looks like AMF logs printing [5gc] [amf] InitialContextSetup, followed by SMF logs showing PDU session establishment and UPF logs creating a PDR/FAR pair.
Common Gotchas
UPF pod CrashLoopBackOff withgtp5g not found. The gtp5g module is missing on the node the UPF was scheduled to. Pin the UPF with a nodeSelector to a node where you loaded the module.
NGAP setup fails, AMF logs show decode error. PLMN mismatch. The MCC/MNC in the gNB config must match what the AMF advertises in served-guami-list and support-tai-list.
PDU session establishment fails with cause 26 (insufficient resources). Almost always the UE pool subnet conflicts with your cluster network or the UPF cannot route N6 traffic. Check the UPF's data-plane interface and IP forwarding (net.ipv4.ip_forward=1).
No DNS for the UE. The UPF does not run DNS. Either NAT the UE pool out and let upstream DNS work, or set the DNS in the SMF config under dnnList.
MongoDB pod evicted. Default chart uses ephemeral storage. Add a PVC and use a real storage class.
What This Buys You
Once stable, you have a 5GC you can break, instrument, and rebuild in minutes. Add Wireshark on the N2 and N3 interfaces, and you can trace NGAP, PFCP, and GTP-U end-to-end. That is the actual point of running this — not running it, but learning how the NFs talk.
Next steps worth exploring: swap UERANSIM for a real gNB over an SDR, enable network slicing with multiple S-NSSAIs, and add Prometheus scraping to the NF metrics endpoints. The chart exposes them already.