While this post will end up with a running Teamspeak server, it is very hard on the resources of the Raspberry Pi and might not be suitable for everyday use.

To deploy a teamspeak server on raspberry pi a few things need to be done:

  • (optional) Get a Kubernetes cluster up and running (this is not required, if you just want to run the docker container directly).
  • Get Teamspeak to run on ARM.
  • Setup an ingress controller to make the Teamspeak server accessible from the outside world (in this case this will be the Nginx Ingress).

Setting up a kubernetes cluster

To setup a kubernetes cluster on Raspberry Pis K3S is very good approach, as the cluster will be more lightweight that simply installing upstream kubernetes.

As a base I recommend using HypriotOS or Ubuntu Server1 as those allow configuring the images using Cloud-Init.

Make sure to follow the instructions to use the the legacy backend for iptables if installing kubernetes v1.17 or lower: kubeadm instructions.

When installing k3s to run Teamspeak Traefik should not be installed, as only the 2.x version supports UDP ingresses - so instead nginx-ingress will be installed later:

1curl -sfL https://get.k3s.io | sh -s - --no-deploy=traefik

Deploy Teamspeak

Build an ARM image for Teamspeak

Teamspeak does not provide a binary for ARM.

It is however possible to run it using Qemu - I have already prepared an ARM image that will run the Teamspeak server through qemu that you can find on my Dockerhub - or if you want to see the source checkout the repository on Github.

The image is using the same entrypoint.sh as the official image - so if you are already using that one you should be able to use it exactly the same way (if not - feel free to open an issue).

Deploy the image

Now - if you do not want to use kubernetes, you can simply use the image using docker and expose the required Teamspeak ports as you would with docker:

1docker run -p 9987:9987/udp -p 10011:10011 -p 30033:30033 -e TS3SERVER_LICENSE=accept monadt/teamspeak3-server

This way is a lot easier on the resources and will likely run more reliable on a Raspberry PI with < 2 GB of RAM.

Setup nginx-ingress

Get nginx-ingress to run on ARM

Setting up the nginx-ingress on a cluster running on ARM needs a few extra steps when using the official documentation.

The images used in the manifests are not compatible with armv7 (that is used when running a cluster on a bunch of Raspberry Pis).

First the mandatory.yaml has to be updated to use the images for the arm architecture2:

1wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.30.0/deploy/static/mandatory.yaml
2sed -i 's/nginx-ingress-controller:0/nginx-ingress-controller-arm:0/' mandatory.yaml

The resulting mandatory.yaml file can now be applied to the cluster:

1kubectl apply -f mandatory.yaml

In my local cluster I am using the NodePort approach, so the service for that can be applied next:

1kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.30.0/deploy/static/provider/baremetal/service-nodeport.yaml

Setup a Teamspeak deployment

With all pieces in place the Teamspeak container can now be deployed onto the cluster:

Save the following yaml into a file (e.g. teamspeak.yaml).

 1---
 2apiVersion: v1
 3kind: Namespace
 4metadata:
 5  name: teamspeak
 6---
 7apiVersion: v1
 8kind: PersistentVolumeClaim
 9metadata:
10  name: teamspeak-pvc
11  namespace: teamspeak
12spec:
13  accessModes:
14    - ReadWriteOnce
15  storageClassName: local-path
16  resources:
17    requests:
18      storage: 256Mi
19---
20apiVersion: apps/v1
21kind: Deployment
22metadata:
23  name: teamspeak-deployment
24  namespace: teamspeak
25  labels:
26    app: teamspeak
27spec:
28  replicas: 1
29  selector:
30    matchLabels:
31      app: teamspeak
32  template:
33    metadata:
34      namespace: teamspeak
35      labels:
36        app: teamspeak
37    spec:
38      containers:
39        - name: teamspeak-server
40          image: monadt/teamspeak3-server:3.11.0
41          ports:
42            - name: ts
43              containerPort: 9987
44              protocol: UDP
45          resources:
46          env:
47          - name: TS3SERVER_LICENSE
48            value: accept
49          volumeMounts:
50          - mountPath: /var/ts3server/
51            name: teamspeak-data
52      volumes:
53        - name: teamspeak-data
54          persistentVolumeClaim:
55            claimName: teamspeak-pvc
56---
57apiVersion: v1
58kind: Service
59metadata:
60  name: teamspeak-service
61  namespace: teamspeak
62  labels:
63    app: teamspeak
64spec:
65  type: ClusterIP
66  ports:
67    - port: 9987
68      targetPort: ts
69      protocol: UDP
70      name: ts
71  selector:
72    app: teamspeak
73---
74apiVersion: v1
75kind: ConfigMap
76metadata:
77  name: udp-services
78  namespace: ingress-nginx
79data:
80  9987: "teamspeak/teamspeak-service:9987"

Apply this using kubectl:

1kubectl apply -f teamspeak.yaml

The 9987 udp port will also need to be added to the ingress service. In the ports section of the service add the following snippet:

1kubectl edit svc ingress-nginx -n ingress-nginx
1- name: teamspeak
2  port: 9987
3  protocol: UDP
4  targetPort: 9987

Forwarding traffic to the ingress

The final step depends a lot on the setup you are deploying the cluster in.

If it is behind your local router, you have to check which port was bound to the 9987 udp port and forward this to one of your cluster-nodes:

1kubectl describe svc ingress-nginx -n ingress-nginx
 1Name:                     ingress-nginx
 2Namespace:                ingress-nginx
 3Labels:                   app.kubernetes.io/name=ingress-nginx
 4                          app.kubernetes.io/part-of=ingress-nginx
 5Annotations:              Selector:  app.kubernetes.io/name=ingress-nginx,app.kubernetes.io/part-of=ingress-nginx
 6Type:                     NodePort
 7IP:                       10.43.33.70
 8Port:                     http  80/TCP
 9TargetPort:               80/TCP
10NodePort:                 http  32224/TCP
11Endpoints:
12Port:                     https  443/TCP
13TargetPort:               443/TCP
14NodePort:                 https  31955/TCP
15Endpoints:
16Port:                     teamspeak  9987/UDP
17TargetPort:               9987/UDP
18NodePort:                 teamspeak  31222/UDP
19Endpoints:
20Session Affinity:         None
21External Traffic Policy:  Cluster
22Events:                   <none>

In this case the port that needs to be forwarded is the 31222 port (the NodePort for the 9987 UDP port).


  1. For Ubuntu you will have to edit the file /boot/firmware/cmdline.txt and add the options cgroup_memory=1 cgroup_enable=memory at the end of the line for k3s (or containers in general) to work. ↩︎

  2. See https://github.com/kubernetes/ingress-nginx/pull/3852 ↩︎