...
Anton Krinner

How to Set Up a Secure Low-Budget Quake III Game Server You Can Play On In Your Browser

November 05, 2023
A simple yet stylish gaming station in a room with soft, warm lighting. The setup features an ultra-wide monitor with a curved screen sitting on a neat, white desk with no clutter. Accompanying the monitor are a sleek keyboard and an ergonomic mouse. The desk hosts a small, green potted plant, and beside it, a gaming computer tower with a transparent side showing a neatly organized interior with tasteful RGB lighting. The screen shows a generic Kubernetes user interface, with graphs and status indicators but devoid of any textual information.

In this guide, we'll walk through setting up a Quake III server on Kubernetes — without the need for expensive load balancers. This setup allows you to play directly in your browser, all on a budget-friendly setup using DigitalOcean's droplets. If you want to have it a little more professional, check out my other blog post where a Load Balancer is used

Quake III, the legendary first-person shooter from 1999, continues to captivate gamers worldwide. Its open-source release in 2005 sparked various enhancements, and among them is QuakeKube. This adaptation harnesses Kubernetes, allowing players to relive Quake III's action-packed arenas right from their browsers. The creators of QuakeKube have packaged everything you need to play in a Kubernetes cluster, making the setup process straightforward.

A little detour on the technical part. QuakeKube basically runs a dedicated quake server from the ioquake3 project which has been written in C and C++ which communicates with the Java Script web client (taken from QuakeJS) via a proxy. This works smoother than the QuakeJS server because it runs in its original programming language. It also allows both browser-based and real-client gaming. I will create another article in the future to explain how both, browser and real-client users can join the same game. Besides QuakeJS, there exist other ports to run the game in the browser like e.g. ioquake3.js, Quake3 and planet_quake.

Prerequisites:

Step-by-Step Guide:

1. Connect to Your Droplet

Option 1: SSH

Use SSH to access your DigitalOcean droplet:

ssh -i /path/to/your/private/key root@<droplet-ip-address>

Replace /path/to/your/private/key with your private key file path and <droplet-ip-address> with your droplet's IP.

Option 2: DigitalOcean Console

Alternatively, utilize the built-in console by DigitalOcean. Head to the “Droplets” section in your DigitalOcean dashboard, select the droplet you wish to access, and click on “Console”.

2: Install Kubernetes with k0s

Execute the following commands, one after the other:

curl -sSLf https://get.k0s.sh | sudo sh
sudo k0s install controller --single
sudo k0s start

Ensure the correctness of your setup by running k0s status, which should display the status of your Kubernetes cluster.

3. Install kubectl

Install kubectl by following this guide. I would recommend to do it via apt.

Add the export line to ~/.bashrc via the following command:

echo 'export KUBECONFIG=/var/lib/k0s/pki/admin.conf' >> ~/.bashrc
                            

Run the following command to apply the changes in the current terminal:

source ~/.bashrc
                            

4. Deploy the Quake Server

Use the following manifest to deploy the Quake servers. This manifest should be in a YAML file. You can create this file by typing nano quake-manifest.yaml and copying and pasting the manifest into the file. Please note, that it is essential to change all the passwords provided in the Quake III Arena server configuration files to ensure security. Close and save with CTRL and X, confirming with a Y.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: quake
spec:
  selector:
    matchLabels:
      run: quake
  replicas: 1
  template:
    metadata:
      labels:
        run: quake
      annotations:
        prometheus.io/scrape: 'true'
        prometheus.io/port: '8080'
    spec:
      containers:
      - command:
        - q3
        - server
        - --config=/config/config.yaml
        - --content-server=http://127.0.0.1:9090
        - --agree-eula
        image: docker.io/criticalstack/quake:latest
        name: server
        ports:
        - containerPort: 8080
        readinessProbe:
          tcpSocket:
            port: 8080
          initialDelaySeconds: 15
          periodSeconds: 5
        volumeMounts:
        - name: quake3-server-config
          mountPath: /config
        - name: quake3-content
          mountPath: /assets
      - command:
        - q3
        - content
        - --seed-content-url=http://content.quakejs.com
        image: docker.io/criticalstack/quake:latest
        name: content-server
        ports:
        - containerPort: 9090
        volumeMounts:
        - name: quake3-content
          mountPath: /assets
      volumes:
        - name: quake3-server-config
          configMap:
            name: quake3-server-config
        - name: quake3-content
          emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
  name: quake
spec:
  type: NodePort
  selector:
    run: quake
  ports:
    - port: 8080
      targetPort: 8080
      nodePort: 30001
      name: client
    - port: 27960
      targetPort: 27960
      nodePort: 30003
      name: server
      protocol: UDP
    - port: 9090
      targetPort: 9090
      nodePort: 30002
      name: content
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: quake3-server-config
data:
  config.yaml: |
    fragLimit: 25
    timeLimit: 15m
    bot:
      minPlayers: 3
    game:
      motd: "Welcome"
      type: FreeForAll
      forceRespawn: false
      inactivity: 10m
      quadFactor: 3
      weaponRespawn: 3
      password: Ei&Y2WvF4N
    server:
      hostname: "quakekube"
      maxClients: 20
      password: 4s%qj0iHEs
    commands:
      - addbot sarge 2
    maps:
    - name: q3dm7
      type: FreeForAll
      timeLimit: 10m
    - name: q3dm17
      type: FreeForAll
    - name: q3wctf1
      type: CaptureTheFlag
      captureLimit: 8
    - name: q3tourney2
      type: FreeForAll
    - name: q3wctf3
      type: CaptureTheFlag
      captureLimit: 8
    - name: ztn3tourney1
      type: FreeForAll
                            

Once you've copied the manifest into a file, you can deploy it with kubectl apply -f quake-manifest.yaml.

5. Install Nginx

Install Nginx with the following commands, one after the other:

sudo apt-get update
sudo apt-get install nginx -y
sudo systemctl start nginx

6. Create a Nginx configuration file

Please replace your domain name and execute the command:

sudo nano /etc/nginx/sites-available/your-domain

Copy the following content into the file, replace your domain and save it with Ctrl + X

server {
    listen 80;
    listen [::]:80;
    server_name your-domain;

    location / {
        proxy_pass http://localhost:30003; # Make sure this points to your Kubernetes service port
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

7. Enable the Nginx configuration and test it.

Please replace your domain name and execute the commands, one after the other:

sudo ln -s /etc/nginx/sites-available/your-domain /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

8. Secure with Certbot

Lastly, secure your server with SSL certificates from Let's Encrypt using Certbot. Please replace your domain name:

sudo apt-get install certbot python3-certbot-nginx -y
sudo certbot --nginx -d your-domain
sudo certbot renew --dry-run

Wrapping Up:

Congratulations! You now have a Quake III server running securely on Kubernetes, accessible for some browser-based fragging action. Customize the setup as needed, and get ready to invite your friends for a match! Remember to replace placeholders like your-domain with your domain name. Happy gaming!