04_lab_4g_epc_docker
Part 4: Docker Lab - Open5GS Core with UERANSIM
Learning Objective: Build a working Open5GS core network and simulate gNB/UE with UERANSIM on Docker.
Architecture Note: Open5GS runs in combined mode — a single deployment supports both 4G EPC and 5G SA interfaces simultaneously. UERANSIM connects as a 5G gNB (via NGAP/N2 to AMF), while the core handles both 4G and 5G subscriber operations internally. This lab focuses on the 5G SA path.
Table of Contents
- Prerequisites
- Lab Architecture
- Docker Network Design
- Step 1: Project Setup
- Step 2: Docker Compose Configuration
- Step 3: Open5GS NF Configurations
- Step 4: UERANSIM Configuration
- Step 5: Launch the Core
- Step 6: Register a Subscriber
- Step 7: Connect gNB and UE
- Step 8: Verify Connectivity
- Packet Capture with tcpdump
- Troubleshooting
Prerequisites
Software
docker --version
# Docker version 24.0.0 or higher
docker compose version
# Docker Compose version v2.20.0 or higher
Disk Space
- ~5GB for Docker images
- ~2GB for logs and data
Docker Images Used
| Component | Image | Source |
|---|---|---|
| MongoDB | mongo:6.0 |
Official MongoDB |
| Open5GS NFs | borieher/open5gs-<nf>:v2.7.6 |
docker-open5gs |
| Open5GS WebUI | borieher/open5gs-webui:v2.7.6 |
docker-open5gs |
| UERANSIM gNB | louisroyer/ueransim-gnb:latest |
louisroyer Docker Hub |
| UERANSIM UE | louisroyer/ueransim-ue:latest |
louisroyer Docker Hub |
There is no official monolithic open5gs/open5gs Docker image. Each NF gets its own container image. Alternative: build from source using the Open5GS Dockerfile.
Lab Architecture
graph TB
subgraph "Docker Host (Mac)"
subgraph "SBI Network (172.22.0.0/24)"
MongoDB[(MongoDB
172.22.0.2)]
WebUI[WebUI
172.22.0.3
:9999]
NRF[NRF
172.22.0.10]
SCP[SCP
172.22.0.11]
AMF[AMF
172.22.0.20]
SMF[SMF
172.22.0.21]
AUSF[AUSF
172.22.0.13]
UDM[UDM
172.22.0.14]
UDR[UDR
172.22.0.15]
PCF[PCF
172.22.0.16]
NSSF[NSSF
172.22.0.17]
BSF[BSF
172.22.0.18]
end
subgraph "RAN Network (172.23.0.0/24)"
UPF[UPF
172.23.0.10]
gNB[UERANSIM gNB
172.23.0.2]
UE[UERANSIM UE
172.23.0.3]
end
end
Internet[🌐 Internet
via ogstun]
UE <-->|RRC| gNB
gNB <-->|N2 NGAP| AMF
gNB <-->|N3 GTP-U| UPF
AMF <-->|SBI| NRF
SMF <-->|SBI| NRF
AUSF <-->|SBI| NRF
SMF <-->|N4 PFCP| UPF
UPF <-->|N6 ogstun| Internet
WebUI <-->|HTTP| MongoDB
UDR <-->|MongoDB| MongoDB
style MongoDB fill:#f9f
style WebUI fill:#9ff
style AMF fill:#fcc
style UPF fill:#cfc
style NRF fill:#ff9Docker Network Design
| Network | Subnet | Purpose |
|---|---|---|
| open5gs_sbi | 172.22.0.0/24 | SBI (HTTP/2) between NFs, MongoDB, WebUI |
| open5gs_ran | 172.23.0.0/24 | N2/N3 between AMF/UPF and UERANSIM |
Step 1: Project Setup
mkdir -p ~/open5gs_lab/{config,ueransim,log}
cd ~/open5gs_lab
Step 2: Docker Compose Configuration
Create docker-compose.yml:
version: '3.8'
services:
# === Infrastructure ===
mongodb:
image: mongo:6.0
container_name: open5gs_mongodb
networks:
open5gs_sbi:
ipv4_address: 172.22.0.2
volumes:
- mongodb_data:/data/db
restart: unless-stopped
webui:
image: borieher/open5gs-webui:v2.7.6
container_name: open5gs_webui
networks:
open5gs_sbi:
ipv4_address: 172.22.0.3
ports:
- "9999:9999"
environment:
- DB_URI=mongodb://172.22.0.2/open5gs
depends_on:
- mongodb
restart: unless-stopped
# === 5G Core NFs ===
nrf:
image: borieher/open5gs-nrf:v2.7.6
container_name: open5gs_nrf
volumes:
- ./config/nrf.yaml:/etc/open5gs/nrf.yaml
networks:
open5gs_sbi:
ipv4_address: 172.22.0.10
restart: unless-stopped
scp:
image: borieher/open5gs-scp:v2.7.6
container_name: open5gs_scp
volumes:
- ./config/scp.yaml:/etc/open5gs/scp.yaml
networks:
open5gs_sbi:
ipv4_address: 172.22.0.11
depends_on:
- nrf
restart: unless-stopped
ausf:
image: borieher/open5gs-ausf:v2.7.6
container_name: open5gs_ausf
volumes:
- ./config/ausf.yaml:/etc/open5gs/ausf.yaml
networks:
open5gs_sbi:
ipv4_address: 172.22.0.13
depends_on:
- nrf
restart: unless-stopped
udm:
image: borieher/open5gs-udm:v2.7.6
container_name: open5gs_udm
volumes:
- ./config/udm.yaml:/etc/open5gs/udm.yaml
networks:
open5gs_sbi:
ipv4_address: 172.22.0.14
depends_on:
- nrf
restart: unless-stopped
udr:
image: borieher/open5gs-udr:v2.7.6
container_name: open5gs_udr
volumes:
- ./config/udr.yaml:/etc/open5gs/udr.yaml
networks:
open5gs_sbi:
ipv4_address: 172.22.0.15
depends_on:
- mongodb
- nrf
restart: unless-stopped
pcf:
image: borieher/open5gs-pcf:v2.7.6
container_name: open5gs_pcf
volumes:
- ./config/pcf.yaml:/etc/open5gs/pcf.yaml
networks:
open5gs_sbi:
ipv4_address: 172.22.0.16
depends_on:
- nrf
restart: unless-stopped
nssf:
image: borieher/open5gs-nssf:v2.7.6
container_name: open5gs_nssf
volumes:
- ./config/nssf.yaml:/etc/open5gs/nssf.yaml
networks:
open5gs_sbi:
ipv4_address: 172.22.0.17
depends_on:
- nrf
restart: unless-stopped
bsf:
image: borieher/open5gs-bsf:v2.7.6
container_name: open5gs_bsf
volumes:
- ./config/bsf.yaml:/etc/open5gs/bsf.yaml
networks:
open5gs_sbi:
ipv4_address: 172.22.0.18
depends_on:
- nrf
restart: unless-stopped
amf:
image: borieher/open5gs-amf:v2.7.6
container_name: open5gs_amf
volumes:
- ./config/amf.yaml:/etc/open5gs/amf.yaml
networks:
open5gs_sbi:
ipv4_address: 172.22.0.20
open5gs_ran:
ipv4_address: 172.23.0.20
depends_on:
- nrf
cap_add:
- NET_ADMIN
restart: unless-stopped
smf:
image: borieher/open5gs-smf:v2.7.6
container_name: open5gs_smf
volumes:
- ./config/smf.yaml:/etc/open5gs/smf.yaml
networks:
open5gs_sbi:
ipv4_address: 172.22.0.21
depends_on:
- nrf
restart: unless-stopped
upf:
image: borieher/open5gs-upf:v2.7.6
container_name: open5gs_upf
volumes:
- ./config/upf.yaml:/etc/open5gs/upf.yaml
networks:
open5gs_sbi:
ipv4_address: 172.22.0.30
open5gs_ran:
ipv4_address: 172.23.0.10
cap_add:
- NET_ADMIN
privileged: true
sysctls:
- net.ipv4.ip_forward=1
restart: unless-stopped
# === UERANSIM ===
ueransim_gnb:
image: louisroyer/ueransim-gnb:latest
container_name: ueransim_gnb
volumes:
- ./ueransim/gnb.yaml:/config/gnb.yaml
networks:
open5gs_ran:
ipv4_address: 172.23.0.2
cap_add:
- NET_ADMIN
depends_on:
- amf
- upf
restart: unless-stopped
ueransim_ue:
image: louisroyer/ueransim-ue:latest
container_name: ueransim_ue
volumes:
- ./ueransim/ue.yaml:/config/ue.yaml
networks:
open5gs_ran:
ipv4_address: 172.23.0.3
cap_add:
- NET_ADMIN
privileged: true
depends_on:
- ueransim_gnb
restart: unless-stopped
networks:
open5gs_sbi:
driver: bridge
ipam:
config:
- subnet: 172.22.0.0/24
open5gs_ran:
driver: bridge
ipam:
config:
- subnet: 172.23.0.0/24
volumes:
mongodb_data:
Mac Docker Note: The privileged: true flag on UPF and UE is needed for TUN device creation (ogstun / uesimtun0). On Linux hosts you can use devices: [/dev/net/tun] instead, but Mac Docker Desktop runs containers inside a Linux VM where /dev/net/tun access requires privileged mode.
Step 3: Open5GS NF Configurations
Create each config file in config/:
NRF (config/nrf.yaml)
logger:
level: info
nrf:
serving:
- plmn_id:
mcc: 001
mnc: 01
sbi:
server:
- address: 172.22.0.10
port: 7777
SCP (config/scp.yaml)
logger:
level: info
scp:
sbi:
server:
- address: 172.22.0.11
port: 7777
client:
nrf:
- uri: http://172.22.0.10:7777
AMF (config/amf.yaml)
logger:
level: info
amf:
sbi:
server:
- address: 172.22.0.20
port: 7777
client:
nrf:
- uri: http://172.22.0.10:7777
scp:
- uri: http://172.22.0.11:7777
ngap:
server:
- address: 172.23.0.20
guami:
- plmn_id:
mcc: 001
mnc: 01
amf_id:
region: 2
set: 1
tai:
- plmn_id:
mcc: 001
mnc: 01
tac: 1
plmn_support:
- plmn_id:
mcc: 001
mnc: 01
s_nssai:
- sst: 1
security:
integrity_order: [NIA2, NIA1, NIA0]
ciphering_order: [NEA2, NEA1, NEA0]
Notice ciphering_order puts NEA2 first (AES-128). Never put NEA0 (null cipher) first in production — that's the downgrade attack from Part 8.
SMF (config/smf.yaml)
logger:
level: info
smf:
sbi:
server:
- address: 172.22.0.21
port: 7777
client:
nrf:
- uri: http://172.22.0.10:7777
scp:
- uri: http://172.22.0.11:7777
pfcp:
server:
- address: 172.22.0.21
client:
upf:
- address: 172.22.0.30
subnet:
- addr: 10.45.0.1/16
dnn: internet
dns:
- 8.8.8.8
- 8.8.4.4
UPF (config/upf.yaml)
logger:
level: info
upf:
pfcp:
server:
- address: 172.22.0.30
gtpu:
server:
- address: 172.23.0.10
subnet:
- addr: 10.45.0.1/16
dnn: internet
AUSF (config/ausf.yaml)
logger:
level: info
ausf:
sbi:
server:
- address: 172.22.0.13
port: 7777
client:
nrf:
- uri: http://172.22.0.10:7777
scp:
- uri: http://172.22.0.11:7777
UDM (config/udm.yaml)
logger:
level: info
udm:
sbi:
server:
- address: 172.22.0.14
port: 7777
client:
nrf:
- uri: http://172.22.0.10:7777
scp:
- uri: http://172.22.0.11:7777
UDR (config/udr.yaml)
logger:
level: info
udr:
sbi:
server:
- address: 172.22.0.15
port: 7777
client:
nrf:
- uri: http://172.22.0.10:7777
scp:
- uri: http://172.22.0.11:7777
db_uri: mongodb://172.22.0.2/open5gs
PCF (config/pcf.yaml)
logger:
level: info
pcf:
sbi:
server:
- address: 172.22.0.16
port: 7777
client:
nrf:
- uri: http://172.22.0.10:7777
scp:
- uri: http://172.22.0.11:7777
db_uri: mongodb://172.22.0.2/open5gs
NSSF (config/nssf.yaml)
logger:
level: info
nssf:
sbi:
server:
- address: 172.22.0.17
port: 7777
client:
nrf:
- uri: http://172.22.0.10:7777
scp:
- uri: http://172.22.0.11:7777
nsi:
- addr: 172.22.0.20
port: 7777
s_nssai:
sst: 1
BSF (config/bsf.yaml)
logger:
level: info
bsf:
sbi:
server:
- address: 172.22.0.18
port: 7777
client:
nrf:
- uri: http://172.22.0.10:7777
scp:
- uri: http://172.22.0.11:7777
Step 4: UERANSIM Configuration
gNB (ueransim/gnb.yaml)
mcc: '001'
mnc: '01'
nci: '0x000000010'
idLength: 32
tac: 1
linkIp: 172.23.0.2
ngapIp: 172.23.0.2
gtpIp: 172.23.0.2
amfConfigs:
- address: 172.23.0.20
port: 38412
slices:
- sst: 1
ignoreStreamIds: true
UERANSIM gNB connects via NGAP (N2, port 38412) to AMF. This is a 5G SA connection. The 4G S1AP interface (port 36412) is not used by UERANSIM.
UE (ueransim/ue.yaml)
supi: 'imsi-001010000000001'
mcc: '001'
mnc: '01'
routingIndicator: '0000'
# No SUCI encryption (null scheme for lab)
protectionScheme: 0
homeNetworkPublicKey: ''
homeNetworkPublicKeyId: 1
# Security keys (must match WebUI registration)
key: '465B5CE8B199B49FAA5F0A2EE238A6BC'
op: 'E8ED289DEBA952E4283B54E88E6183CA'
opType: 'OPC'
gnbSearchList:
- 172.23.0.2
sessions:
- type: 'IPv4'
apn: 'internet'
slice:
sst: 1
integrity:
IA1: true
IA2: true
IA3: true
ciphering:
EA1: true
EA2: true
EA3: true
Step 5: Launch the Core
# Start infrastructure first
docker compose up -d mongodb
sleep 3
# Start NRF (service registry must be up before other NFs)
docker compose up -d nrf
sleep 2
# Start all other NFs
docker compose up -d scp ausf udm udr pcf nssf bsf amf smf upf webui
# Verify all containers are running
docker compose ps
Check NRF registrations
docker compose logs nrf | grep -i "registered"
Expected output:
NF registered: AMF (172.22.0.20)
NF registered: SMF (172.22.0.21)
NF registered: AUSF (172.22.0.13)
NF registered: UDM (172.22.0.14)
NF registered: PCF (172.22.0.16)
Step 6: Register a Subscriber
- Open browser: http://localhost:9999
- Login:
- Username:
admin - Password:
1423
- Username:
- Click Subscribers → + (Add)
- Fill in subscriber details:
- IMSI:
001010000000001 - K:
465B5CE8B199B49FAA5F0A2EE238A6BC - OPc:
E8ED289DEBA952E4283B54E88E6183CA - APN/DNN:
internet - SST:
1
- IMSI:
- Click SAVE
The K/OPc values in the WebUI must exactly match the values in ue.yaml. A single character mismatch causes authentication failure (check AMF logs for MAC failure).
Step 7: Connect gNB and UE
Start gNB:
docker compose up -d ueransim_gnb
docker compose logs -f ueransim_gnb
Expected output:
[sctp] [info] Trying to establish SCTP connection... (172.23.0.20:38412)
[sctp] [info] SCTP connection established
[ngap] [info] NG Setup procedure is successful
Start UE:
docker compose up -d ueransim_ue
docker compose logs -f ueransim_ue
Expected output:
[nas] [info] UE switches to state [MM-REGISTERED/NORMAL-SERVICE]
[nas] [info] PDU Session establishment is successful PSI[1]
[app] [info] Connection setup for PDU session[1] is successful, TUN interface[uesimtun0, 10.45.0.2] is up.
Step 8: Verify Connectivity
Check UE Interface
docker exec -it ueransim_ue ip addr show uesimtun0
Output:
5: uesimtun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1400
inet 10.45.0.2/32 scope global uesimtun0
Ping Test
docker exec -it ueransim_ue ping -I uesimtun0 -c 4 8.8.8.8
Output:
PING 8.8.8.8 (8.8.8.8) from 10.45.0.2 uesimtun0: 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=118 time=12.3 ms
✅ Success! Your 5G core is working end-to-end.
Packet Capture with tcpdump
Capture NGAP (N2 Control Plane)
docker exec -it open5gs_amf tcpdump -i any -w /tmp/ngap.pcap sctp
# Copy out: docker cp open5gs_amf:/tmp/ngap.pcap .
Wireshark filter: ngap
Capture GTP-U (N3 User Plane)
docker exec -it open5gs_upf tcpdump -i any -w /tmp/gtpu.pcap udp port 2152
# Copy out: docker cp open5gs_upf:/tmp/gtpu.pcap .
Wireshark filter: gtp
Capture SBI (HTTP/2 between NFs)
docker exec -it open5gs_amf tcpdump -i any -w /tmp/sbi.pcap tcp port 7777
# Copy out: docker cp open5gs_amf:/tmp/sbi.pcap .
Wireshark filter: http2
Troubleshooting
NFs Cannot Register with NRF
docker compose logs nrf | tail -20
docker compose logs amf | grep -i "error\|failed"
Common causes:
- NRF not started before other NFs → restart NFs after NRF is up
- Wrong NRF URI in config → verify
http://172.22.0.10:7777
UE Cannot Authenticate
docker compose logs amf | grep -i "mac\|auth\|fail"
Common causes:
- K/OPc mismatch between WebUI and ue.yaml
- IMSI not registered → check WebUI subscriber list
- PLMN mismatch (MCC/MNC)
No Internet from UE
# Check UPF TUN interface
docker exec -it open5gs_upf ip addr show ogstun
# Check UPF routing
docker exec -it open5gs_upf ip route
# Check NAT
docker exec -it open5gs_upf iptables -t nat -L
Fix: UPF needs NAT for outbound traffic:
docker exec -it open5gs_upf iptables -t nat -A POSTROUTING \
-s 10.45.0.0/16 ! -o ogstun -j MASQUERADE
Cleanup
docker compose down -v # -v removes volumes (subscriber data)
🔬 Exercises
- Multi-UE: Add a second UE (
imsi-001010000000002) with different security keys. Register it in the WebUI and modifydocker-compose.ymlto addueransim_ue2. - IP Pool: Change UPF/SMF subnet from
10.45.0.0/16to10.50.0.0/16. What do you need to update? - Wireshark Analysis: Capture NGAP traffic and find the
InitialUEMessage. What identifiers can you see? Is SUPI visible? (Hint: checkprotectionSchemein ue.yaml) - DNS Test: From the UE, run
docker exec ueransim_ue nslookup google.com 8.8.8.8through the tunnel. - NRF Discovery: Capture SBI traffic on port 7777 and observe how AMF discovers SMF via NRF during PDU session setup.
Summary
You have successfully:
- ✅ Deployed a full 5G SA core with all NFs (NRF, SCP, AMF, SMF, UPF, AUSF, UDM, UDR, PCF, NSSF, BSF)
- ✅ Configured NRF-based service discovery (SBA)
- ✅ Registered a subscriber via WebUI
- ✅ Connected UERANSIM gNB via NGAP (N2) and UE
- ✅ Established a PDU session and verified Internet connectivity
- ✅ Captured packets on N2, N3, and SBI interfaces
Next: Part 5: 5G NSA Concepts →