I have been running UniFi hardware at home for the past year and I'm pretty happy with how it has been performing. I was previously running Cisco Meraki for all my home networking needs, until the subscription renewal was due and I didn't fancy selling one of my kidneys.
When initally looking into the UniFi solution, I noticed that the Cloud Key product was optional and you could actually run and manage the features it provides yourself. The thought of saving money and hacking about with a Raspberry Pi was just too enticing!
This setup has been, touch wood, pretty rock solid for me and not something I was looking to change until I came across a SaaS product called Hostifi. This product was a fully managed cloud version of the controller, no more backup worries and securely accessible from anywhere! I really love this as a solution and I can see why it's such a good fit for many companies.
Hello Civo...
Having worked with Civo for over a year on their Kubernetes project as a Beta tester and Civo Ambassador, I thought why not try and deploy the UniFi controller to their managed Kubernetes platform. Also going one better and adding it to the Civo Kubernetes Marketplace for quick and simple deployment for others.
Getting Started
If you haven't already, get yourself over to https://dashboard.civo.com/signup and sign up, there is a $250 free credit offer on at the moment too!
Prerequisites
This guide assumes you are up to speed with the basics of the Civo platform and are proficient in using Kubectl. If you are new to Civo and Kubernetes then I recommend reading following "getting started" guide: Getting Started with Civo
Spinning up the cluster
In this guide I will spin up a new cluster using Civo CLI without the default Traefik ingress controller installed, this is so I can deploy Traefik2 later in this guide. This is the command to run in your terminal:
civo k3s create --save --merge -s g3.k3s.medium -w -r Traefik
This will create a new cluster, save and merge it to your KUBECONFIG file.
Once it's created, switch to the new cluster using kubectx (replace the name with your own, mine in this example is called spring-shape):
kubectx spring-shape
Once the new cluster is running we will need to deploy the UniFi Controller itself:
civo k3s application add Unifi-network-controller --cluster spring-shape
After a few minutes you should see all the applications installed and running when you run:
kubectl get pods -A
Alternatively, you can use Civo Dashboard or Terraform, follow the following creating a cluster on Civo documentation.
Deploying Traefik
At time of writing I could only get this to work using my own Traefik manifest and not with the Civo marketplace app. If you happen to get this working with the marketplace app then please let me know and I will update this guide.
Create a new file called traefik-deployment.yaml
, copy and paste the following code substituting the the email address for the SSL certificate with your own.
apiVersion: traefik.containo.us/v1alpha1
kind: ServersTransport
metadata:
name: mytransport
namespace: default
spec:
insecureSkipVerify: true
# All resources definition must be declared
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: ingressroutes.traefik.containo.us
spec:
group: traefik.containo.us
version: v1alpha1
names:
kind: IngressRoute
plural: ingressroutes
singular: ingressroute
scope: Namespaced
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: middlewares.traefik.containo.us
spec:
group: traefik.containo.us
version: v1alpha1
names:
kind: Middleware
plural: middlewares
singular: middleware
scope: Namespaced
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: ingressroutetcps.traefik.containo.us
spec:
group: traefik.containo.us
version: v1alpha1
names:
kind: IngressRouteTCP
plural: ingressroutetcps
singular: ingressroutetcp
scope: Namespaced
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: ingressrouteudps.traefik.containo.us
spec:
group: traefik.containo.us
version: v1alpha1
names:
kind: IngressRouteUDP
plural: ingressrouteudps
singular: ingressrouteudp
scope: Namespaced
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: tlsoptions.traefik.containo.us
spec:
group: traefik.containo.us
version: v1alpha1
names:
kind: TLSOption
plural: tlsoptions
singular: tlsoption
scope: Namespaced
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: tlsstores.traefik.containo.us
spec:
group: traefik.containo.us
version: v1alpha1
names:
kind: TLSStore
plural: tlsstores
singular: tlsstore
scope: Namespaced
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: traefikservices.traefik.containo.us
spec:
group: traefik.containo.us
version: v1alpha1
names:
kind: TraefikService
plural: traefikservices
singular: traefikservice
scope: Namespaced
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: serverstransports.traefik.containo.us
spec:
group: traefik.containo.us
version: v1alpha1
names:
kind: ServersTransport
plural: serverstransports
singular: serverstransport
scope: Namespaced
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: traefik-ingress-controller
rules:
- apiGroups:
- ""
resources:
- services
- endpoints
- secrets
verbs:
- get
- list
- watch
- apiGroups:
- extensions
- networking.k8s.io
resources:
- ingresses
- ingressclasses
verbs:
- get
- list
- watch
- apiGroups:
- extensions
resources:
- ingresses/status
verbs:
- update
- apiGroups:
- traefik.containo.us
resources:
- middlewares
- ingressroutes
- traefikservices
- ingressroutetcps
- ingressrouteudps
- tlsoptions
- tlsstores
- serverstransports
verbs:
- get
- list
- watch
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: traefik-ingress-controller
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: traefik-ingress-controller
subjects:
- kind: ServiceAccount
name: traefik-ingress-controller
namespace: default
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: traefik-ingress-controller
---
kind: Deployment
apiVersion: apps/v1
metadata:
name: traefik
labels:
app: traefik
spec:
replicas: 1
selector:
matchLabels:
app: traefik
template:
metadata:
labels:
app: traefik
spec:
serviceAccountName: traefik-ingress-controller
containers:
- name: traefik
image: traefik:v2.4
args:
- --log.level=DEBUG
- --serversTransport.insecureSkipVerify=true
- --accesslog
- --entrypoints.web.address=:80
- --entrypoints.websecure.Address=:443
- --entrypoints.unifistun.Address=:3478/udp
- --entrypoints.unifiinform.Address=:8080
- --providers.kubernetescrd
- --certificatesresolvers.myresolver.acme.tlschallenge
- --certificatesresolvers.myresolver.acme.email=me@email.co.uk
- --certificatesresolvers.myresolver.acme.storage=acme.json
ports:
- name: web
containerPort: 80
- name: admin
containerPort: 8080
- name: tcpep
containerPort: 8000
- name: udpep
containerPort: 9000
- name: udp-unifi-stun
containerPort: 3478
---
apiVersion: v1
kind: Service
metadata:
name: traefik-tcp
spec:
type: LoadBalancer
selector:
app: traefik
ports:
- protocol: TCP
port: 80
name: web
targetPort: 80
- protocol: TCP
port: 443
name: websecure
targetPort: 443
- protocol: TCP
port: 8443
name: unifisecure
targetPort: 8443
- protocol: TCP
port: 8080
name: admin
targetPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: traefik-udp
spec:
type: LoadBalancer
selector:
app: traefik
ports:
- protocol: UDP
port: 9000
name: udpep
targetPort: 9000
- protocol: UDP
port: 3478
name: udp-unifi-stun
targetPort: 3478
Apply the above file:
kubectl apply -f traefik-deployment
Next you will need to create the ingress rules:
Remember to substitute the url with your own cluster's URL where required below. You can get this from the cluster dashboard or by running
civo k3s show your-cluster-name
:
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: ingressroute-unifi-ui
namespace: unifi
spec:
entryPoints:
- websecure
routes:
- match: Host(`unifi.your_cluster_address`)
kind: Rule
services:
- name: unifi-srv
port: 8443
scheme: https
tls:
certResolver: myresolver
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: ingressroute-unifi-commms
namespace: unifi
spec:
entryPoints:
- unifiinform
routes:
- match: Host(`unifi.your_cluster_address`) && PathPrefix(`/inform`)
kind: Rule
services:
- name: unifi-srv
port: 8080
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRouteUDP
metadata:
name: ingressroute-unifi-stun
namespace: unifi
spec:
entryPoints:
- unifistun
routes:
- match: Host(`unifi.your_cluster_address`) && PathPrefix(`/inform`)
kind: Rule
services:
- name: unifi-srv
port: 3478
You can now test the UI by opening the URL in a web browser:
https://unifi.yourclusteraddress
Controller Setup
You should now be greeted by the UniFi Controller setup page:
)
You can follow through the steps and select the options you require, if you are already running a controller you can also restore the config from a backup.
Re-pointing your devices
Because the controller is not on the same network as the devices, the easiest thing to do is manually re-point each one to the new controller address. This can be done by connecting via SSH into each device and running the following, substituting the URL for your new cluster's URL:
set-inform http://unifi.example.com:8080/inform
Once you have done this, the devices should start to turn green on your dashboard.
If you have any questions about this guide then please reach out to me on twitter:
Keith Hubner or catch me in the Civo community Slack!