Deploying Unifi Controller on kubernetes
July 1, 2023-3 min read
Intro
Recently I started migrating all my selfhosted applications from standalone docker containers to kubernetes. The application that gave me the most trouble to migrate was Ubiquiti's Unifi Controller. In this post I'll describe how to deploy the Unifi Controller on kubernetes and access it's console through a Traefik reverse proxy.
Deploying UniFi Controller
Namespace
apiVersion: v1kind: Namespacemetadata:name: unifi-controller
Just the namespace; nothing special here.
Persistent Volume Claim
apiVersion: v1kind: PersistentVolumeClaimmetadata:name: unifi-longhorn-pvcnamespace: unifi-controllerspec:storageClassName: longhornaccessModes:- ReadWriteManyresources:requests:storage: 5Gi
For storage I'm using a Longhorn volume, but of course you can use anything you prefer.
Deployment
apiVersion: apps/v1kind: Deploymentmetadata:labels:app: unifi-controllername: unifi-controllernamespace: unifi-controllerspec:replicas: 1selector:matchLabels:app: unifi-controllertemplate:metadata:labels:app: unifi-controllerspec:containers:- image: lscr.io/linuxserver/unifi-controller:latestname: unifi-controllerresources:requests:cpu: "100m"memory: "1024Mi"limits:cpu: "500m"memory: "2048Mi"ports:- name: device-commcontainerPort: 8080protocol: TCP- name: stuncontainerPort: 3478protocol: UDP- name: default-consolecontainerPort: 8443protocol: TCP- name: secure-redirectcontainerPort: 8843protocol: TCP- name: http-redirectcontainerPort: 8880protocol: TCP- name: speedtestcontainerPort: 6789protocol: TCP- name: unifi-disccontainerPort: 10001protocol: UDP- name: unifi-disc-l2containerPort: 1900protocol: UDPvolumeMounts:- name: unifi-controller-configmountPath: /configenv:- name: TZvalue: Europe/Athensvolumes:- name: unifi-controller-configpersistentVolumeClaim:claimName: unifi-longhorn-pvc
The deployment is pretty straightforward.
Services
apiVersion: v1kind: Servicemetadata:name: unifi-controllernamespace: unifi-controllerspec:selector:app: unifi-controllerports:- name: stunport: 3478protocol: UDP- name: secure-redirectport: 8843protocol: TCP- name: http-redirectport: 8880protocol: TCP- name: speedtestport: 6789protocol: TCP- name: unifi-discport: 10001protocol: UDP- name: unifi-disc-l2port: 1900protocol: UDP---apiVersion: v1kind: Servicemetadata:name: unifi-controller-uinamespace: unifi-controllerannotations:traefik.ingress.kubernetes.io/service.serversscheme: httpstraefik.ingress.kubernetes.io/service.serverstransport: unifi-controller-unifi-controller-ui@kubernetescrdspec:selector:app: unifi-controllerports:- name: default-consoleport: 8443protocol: TCP---apiVersion: v1kind: Servicemetadata:name: unifi-controller-dev-comnamespace: unifi-controllerannotations:metallb.universe.tf/loadBalancerIPs: 10.0.0.242spec:type: LoadBalancerselector:app: unifi-controllerports:- name: device-commport: 8080protocol: TCP---apiVersion: traefik.containo.us/v1alpha1kind: ServersTransportmetadata:name: unifi-controller-uinamespace: unifi-controllerspec:insecureSkipVerify: true
Defining the services for the controller UI console and for device communication was what gave the most trouble.
Controller UI
The UI console is accessed on port 8443 and requires a HTTPS connection. Furthermore, the console's SSL certificate is self signed.
To accomodate for this, I defined a separate service for the UI console, unifi-controller-ui
, and added the traefik.ingress.kubernetes.io/service.serversscheme: https
annotation so that Traefik will establish a HTTPS connection with the service.
I then created a ServersTransport
object with the insecureSkipVerify: true
option and added referenced it in the service's traefik.ingress.kubernetes.io/service.serverstransport
annotation. This makes Traefik ignore the self signed certificate.
Device communication
The service for device communication was more straightforward. I created the unifi-controller-dev-com
service, and since I'm using Metallb I created a service of type LoadBalancer and set it's IP with the annotation metallb.universe.tf/loadBalancerIPs
After the controller is up and running, I changed the Inform Host in the settings to the same IP, so that the devices will be able to communicate with the controller:
With this, the network devices will be able to be adopted properly by the controller.
Ingress
apiVersion: networking.k8s.io/v1kind: Ingressmetadata:name: unifi-controllernamespace: unifi-controllerannotations:traefik.ingress.kubernetes.io/router.entrypoints: websecurecert-manager.io/cluster-issuer: "lets-encrypt-dns"spec:tls:- hosts:- *redacted*secretName: tls-unifi-ingress-dnsrules:- host: *redacted*http:paths:- path: /pathType: Prefixbackend:service:name: unifi-controller-uiport:number: 8443
Finally I created an Ingress object to access the Controller's UI. Since I'm using cert-manager to automatically issue Let's Encrypt
certificates, I added the relevant annotations to the object.
Conclusion
And that's it! It took some troubleshooting but I finally managed to run Unifi Controller on kubernetes! 🎉