Da ich mein Homelab noch weiter automatisieren wollte habe ich beschlossen anstelle eines Docker-Hosts einen Single-Node-K8S-Cluster aufzusetzen.
Als Kubernetes-Distribution habe ich mich für RKE2 entschieden. RKE2 ist einfach zu installieren und gut supported. Die mitgelieferte Kubernetes-Version ist meist sehr aktuell. Schon die Standard-Installation bringt einen guten Umfang mit, mit dem man sofort arbeiten kann.
Da ich als Container-Netzwerk aber Cilium in der neusten Version, BGP und Envoy Gateway nutzen möchte sind bei der Installation ein paar Dinge anzupassen.
Voraussetzungen:
- BGP-Konfiguration am Router (hier am Beispiel eines Unifi Cloud Gateway Fiber)
- dedizierter Server mit installiertem Linux-Betriebssystem (in meinem Fall ein ASUS PN51-E1-Mini-PC mit openSUSE 16)
Vorbereitung am Router – BGP Konfiguration:
Die BGP-Konfiguration speichere ich in der Datei bgp.conf um sie anschliessend im UI des Gateway hoch zu laden.
frr version 9.1
frr defaults traditional
hostname UniFi-Gateway
log syslog informational
service integrated-vtysh-config
!
router bgp 65100
bgp router-id 192.168.1.1
no bgp ebgp-requires-policy
neighbor RKE2 peer-group
neighbor RKE2 remote-as 65200
neighbor 192.168.1.238 peer-group RKE2
address-family ipv4 unicast
neighbor RKE2 activate
neighbor RKE2 next-hop-self
redistribute connected
exit-address-family
!
endbgp.confDie Konfiguration importieren wir unter «Einstellungen ⚙️ => Richtlinientabelle 🪄 => Dynamisches Routing => BGP-Konfiguration importieren«

Nach einem Klick auf den Hinzufügen-Button ist die Konfiguration im Gateway und wird automatisch geladen.
Jetzt können wir mit der Installation auf dem Server beginnen.
K8S Installation auf dem Server
# RKE2 installieren
curl -sfL https://get.rke2.io | sudo INSTALL_RKE2_TYPE="server" shZsh[WARN] /usr/local is read-only or a mount point; installing to /opt/rke2
[INFO] finding release for channel stable
[INFO] using v1.34.3+rke2r1 as release
[INFO] downloading checksums at https://github.com/rancher/rke2/releases/download/v1.34.3%2Brke2r1/sha256sum-amd64.txt
[INFO] downloading tarball at https://github.com/rancher/rke2/releases/download/v1.34.3%2Brke2r1/rke2.linux-amd64.tar.gz
[INFO] verifying tarball
[INFO] unpacking tarball file to /opt/rke2
[INFO] updating tarball contents to reflect install path
[INFO] moving systemd units to /etc/systemd/system
[INFO] install complete; you may want to run: export PATH=$PATH:/opt/rke2/binZshBevor wir RKE2 starten, passen wir die Konfiguration an:
# Verzeichnis für die Konfiguration anlegen
sudo mkdir -p /etc/rancher/rke2
# Konfigurationsdatei erstellen
cat <<EOF | sudo tee /etc/rancher/rke2/config.yaml
cni: none
disable-kube-proxy: true
disable:
- rke2-canal
- rke2-ingress-nginx # (Da wir Envoy nutzen wollen)
EOFZshJetzt starten wir RKE2 und sorgen gleich dafür, dass es bei einem Server-Neustart immer automatisch gestartet wird:
sudo systemctl enable --now rke2-server.serviceZshDie .kube-config von RKE2 befindet sich hier:
/etc/rancher/rke2/rke2.yamlZshIch kopiere diese Datei immer auf meinen lokalen Rechner und passe die Namen und URL entsprechend an um bequemer mit dem Cluster zu agieren.
Der Cluster sollte nach kurzer Zeit erreichbar sein, aber in einem «NotReady»-Status, weil kein Cluster Network Interface (CNI) installiert ist:
❯ k get nodes
NAME STATUS ROLES AGE VERSION
asus-pn51-e1 NotReady control-plane,etcd 79s v1.34.3+rke2r1Zsh❯ k describe node asus-pn51-e1
RenewTime: Sun, 11 Jan 2026 19:21:35 +0100
Conditions:
Type Status LastHeartbeatTime LastTransitionTime Reason Message
---- ------ ----------------- ------------------ ------ -------
EtcdIsVoter True Sun, 11 Jan 2026 19:18:41 +0100 Sun, 11 Jan 2026 19:18:41 +0100 MemberNotLearner Node is a voting member of the etcd cluster
MemoryPressure False Sun, 11 Jan 2026 19:21:34 +0100 Sun, 11 Jan 2026 19:18:31 +0100 KubeletHasSufficientMemory kubelet has sufficient memory available
DiskPressure False Sun, 11 Jan 2026 19:21:34 +0100 Sun, 11 Jan 2026 19:18:31 +0100 KubeletHasNoDiskPressure kubelet has no disk pressure
PIDPressure False Sun, 11 Jan 2026 19:21:34 +0100 Sun, 11 Jan 2026 19:18:31 +0100 KubeletHasSufficientPID kubelet has sufficient PID available
Ready False Sun, 11 Jan 2026 19:21:34 +0100 Sun, 11 Jan 2026 19:18:31 +0100 KubeletNotReady container runtime network not ready: NetworkReady=false reason:NetworkPluginNotReady message:Network plugin returns error: cni plugin not initialized
Addresses:
InternalIP: 192.168.1.238
Hostname: asus-pn51-e1
Capacity:
cpu: 16
ephemeral-storage: 485825Mi
hugepages-1Gi: 0ZshUm Cilium zu installieren benutze ich die Cilium-CLI (diese kann man am einfachsten mit Brew installieren: brew install cilium-cli)
cilium install \
--version 1.18.5 \
--set bgpControlPlane.enabled=true \
--set bgpControlPlane.defaultInstance.type=cluster \
--set kubeProxyReplacement=true \
--set operator.replicas=1 \
--set gatewayAPI.enabled=trueZshNach kurzer Zeit ist das Cilium CNI installiert und einsatzbereit:
❯ cilium status
/¯¯\
/¯¯\__/¯¯\ Cilium: OK
\__/¯¯\__/ Operator: OK
/¯¯\__/¯¯\ Envoy DaemonSet: OK
\__/¯¯\__/ Hubble Relay: disabled
\__/ ClusterMesh: disabled
DaemonSet cilium Desired: 1, Ready: 1/1, Available: 1/1
DaemonSet cilium-envoy Desired: 1, Ready: 1/1, Available: 1/1
Deployment cilium-operator Desired: 1, Ready: 1/1, Available: 1/1
Containers: cilium Running: 1
cilium-envoy Running: 1
cilium-operator Running: 1
clustermesh-apiserver
hubble-relay
Cluster Pods: 4/4 managed by Cilium
Helm chart version: 1.18.5
Image versions cilium quay.io/cilium/cilium:v1.18.5@sha256:2c92fb05962a346eaf0ce11b912ba434dc10bd54b9989e970416681f4a069628: 1
cilium-envoy quay.io/cilium/cilium-envoy:v1.34.12-1765374555-6a93b0bbba8d6dc75b651cbafeedb062b2997716@sha256:3108521821c6922695ff1f6ef24b09026c94b195283f8bfbfc0fa49356a156e1: 1
cilium-operator quay.io/cilium/operator-generic:v1.18.5@sha256:36c3f6f14c8ced7f45b40b0a927639894b44269dd653f9528e7a0dc363a4eb99: 1ZshAuch der Cluster-Status sollte sich zu «Ready» geändert haben:
❯ k get nodes
NAME STATUS ROLES AGE VERSION
asus-pn51-e1 Ready control-plane,etcd 13m v1.34.3+rke2r1ZshJetzt können wir Envoy Gateway installieren. Das mache ich mit Helm:
helm install eg oci://docker.io/envoyproxy/gateway-helm --version v1.6.1 -n envoy-gateway-system --create-namespaceZshPulled: docker.io/envoyproxy/gateway-helm:v1.6.1
Digest: sha256:6ede7b1df2938132758290dc8e8038d1a8c11b9fda36917c9bc15da2d06bc85a
NAME: eg
LAST DEPLOYED: Sun Jan 11 19:44:57 2026
NAMESPACE: envoy-gateway-system
STATUS: deployed
REVISION: 1
DESCRIPTION: Install complete
TEST SUITE: None
NOTES:
**************************************************************************
*** PLEASE BE PATIENT: Envoy Gateway may take a few minutes to install ***
**************************************************************************
Envoy Gateway is an open source project for managing Envoy Proxy as a standalone or Kubernetes-based application gateway.
Thank you for installing Envoy Gateway! 🎉
Your release is named: eg. 🎉
Your release is in namespace: envoy-gateway-system. 🎉
To learn more about the release, try:
$ helm status eg -n envoy-gateway-system
$ helm get all eg -n envoy-gateway-system
To have a quickstart of Envoy Gateway, please refer to https://gateway.envoyproxy.io/latest/tasks/quickstart.
To get more details, please visit https://gateway.envoyproxy.io and https://github.com/envoyproxy/gateway.ZshNach kurzer Zeit ist auch Envoy bereit und wir können mit «egctl» (brew install egctl) das Admin-Dashboard öffnen:
❯ egctl x dashboard eg
Found Envoy Gateway pod: envoy-gateway-64d8866b44-kz9tc
Setting up port-forward to Envoy Gateway admin server...
Envoy Gateway admin console URL: http://localhost:56859
Opening browser to access the Envoy Gateway admin console...
Press Ctrl+C to quit
http://localhost:56859Zsh
Damit haben wir die grundlegende Installation des Clusters abgeschlossen und haben jetzt:
- RKE2 in der aktuellsten Version
- Cilium als CNI mit Unterstützung für Gateway API und BGP
- Envoy Gateway
Jetzt können wir noch kurz ein Gateway zum Test erstellen:
# 1. Definiert, WAS angekündigt wird (LoadBalancer IPs)
apiVersion: cilium.io/v2
kind: CiliumBGPAdvertisement
metadata:
name: bgp-advertisement
labels:
bgp.cilium.io/advertise: loadbalancer-services
spec:
advertisements:
- advertisementType: "Service"
service:
addresses:
- LoadBalancerIP
selector:
matchLabels: {}
---
# 2. Definiert, an WEN wir senden (Dein UniFi Router)
apiVersion: cilium.io/v2
kind: CiliumBGPPeerConfig
metadata:
name: unifi-peer-config
spec:
families:
- afi: ipv4
safi: unicast
advertisements:
matchLabels:
bgp.cilium.io/advertise: loadbalancer-services
gracefulRestart:
enabled: true
---
# 3. Verknüpft alles mit deinem Node
apiVersion: cilium.io/v2
kind: CiliumBGPClusterConfig
metadata:
name: bpg-cluster-config
spec:
nodeSelector:
matchLabels:
kubernetes.io/os: linux
bgpInstances:
- name: "asus-pn51-e1"
localASN: 65200
peers:
- name: "unifi-router"
peerAddress: 192.168.1.1
peerASN: 65100 # Hier gehört die Remote-ASN jetzt hin!
peerConfigRef:
name: unifi-peer-config
---
# 4. Aus diesem Pool werden die IP-Adressen genommen
apiVersion: "cilium.io/v2alpha1"
kind: CiliumLoadBalancerIPPool
metadata:
name: "envoy-gateway-pool"
spec:
blocks:
- cidr: "192.168.200.240/28"
serviceSelector:
matchLabels: {}
---
# 5. Gateway-Klassen-Definition
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: envoy-gateway-class
spec:
controllerName: gateway.envoyproxy.io/gatewayclass-controller
---
# 6. Das Gateway, über das wir unsere Routen erreichbar machen
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: external-gateway
namespace: default
spec:
gatewayClassName: envoy-gateway-class
listeners:
- name: http
protocol: HTTP
port: 80
allowedRoutes:
namespaces:
from: AllZshWir sollten jetzt unser Gateway sehen und die IP-Adresse über die es jetzt erreichbar ist. Manchmal dauert es etwas bis es eine externe IP vom Unifi Gateway bekommt. Aber spätestens nach 1-2 Minuten sollte die Ausgabe so aussehen.
❯ k get gtw
NAME CLASS ADDRESS PROGRAMMED AGE
external-gateway envoy-gateway-class 192.168.200.240 True 122mZshUnd anschliessend noch eine kleine Test-Applikation:
# 1. Ein einfacher Test-Pod + Service
apiVersion: v1
kind: Pod
metadata:
name: whoami
labels:
app: whoami
spec:
containers:
- name: whoami
image: traefik/whoami
---
apiVersion: v1
kind: Service
metadata:
name: whoami-service
spec:
selector:
app: whoami
ports:
- port: 80
---
# 2. Die Route zum Gateway
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: whoami-route
spec:
parentRefs:
- name: external-gateway
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: whoami-service
port: 80ZshJetzt ist unsere kleine Demo-App über das Envoy Gateway erreichbar:

Damit haben wir den Grundstein für unseren K8S Server gelegt und können jetzt weitere Tools und Applikationen installieren.
Als nächstes werden wir Tools wie External-DNS installieren um automatisch A-Records im DNS-Server zu erstellen. Dann werden werden wir mit Cert-Manager Let’s-Encrypt-Zertifikate installieren um unsere Apps über HTTPS erreichen zu können.