Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Getting Started

QuakeKube is installed with its Helm chart, which deploys:

  • the controller, which runs game servers for QuakeServer objects and a Quake 3 master server that lists them for native clients
  • the gateway, which serves the web app, where people create servers and play in the browser

Install

The chart is published to GitHub’s container registry and works without any configuration:

helm install quake-kube oci://ghcr.io/chrisrx/quake-kube \
  --namespace quake-kube --create-namespace

The chart is published from every change to the main branch, and deploys the newest images. helm upgrade with the same command updates an install to the newest chart and images.

Then reach the web app through a port-forward:

kubectl --namespace quake-kube port-forward service/quake-kube-gateway 8080:80

Open http://localhost:8080, pick a game, mode and map under New server, and create it. Once it’s ready, Play joins it in the browser. The games are the Quake 3 demo, and on Kubernetes 1.35 and later, Urban Terror and CPMA (see Add Mods And Maps).

The game servers use the files from the Quake 3 demo, which are covered by the Quake 3 demo EULA. Installing QuakeKube means agreeing to it. Browser clients also show it to players before downloading the files.

Native clients

Game servers and the master server get LoadBalancer Services, so native clients can reach them on clusters with load balancers, from a cloud provider or MetalLB. Without one, servers still run and play in the browser. Their Services just wait for an address. gameServers.exposure in the chart’s values switches game servers to NodePort or host ports.

Any Quake 3 client can join a server at the address in its status:

kubectl get quakeservers --all-namespaces

The controller’s master server lists every server that has an address, so clients can find them in their server browser instead. Find its address with:

kubectl --namespace quake-kube get service quake-kube-master

and set it as a master server from the client’s console:

seta sv_master1 "<master address>"

Publish the web app

To make the web app public, put it behind an ingress with a TLS certificate, e.g. from cert-manager, and turn on WebTransport. Browsers only use WebTransport from HTTPS pages. It carries game traffic in unreliable datagrams, like the UDP the game was built for, where the WebSocket it otherwise falls back to holds everything up behind a lost packet.

gateway:
  ingress:
    enabled: true
    className: traefik
    host: quake.example.com
    annotations:
      cert-manager.io/cluster-issuer: letsencrypt
    tls:
      - secretName: quake-kube-web-tls
        hosts: [quake.example.com]
  webtransport:
    enabled: true
helm upgrade quake-kube oci://ghcr.io/chrisrx/quake-kube \
  --namespace quake-kube --values values.yaml

WebTransport runs over HTTP/3, so the gateway serves it from its own UDP LoadBalancer Service, quake-kube-gateway-webtransport, rather than through the ingress. Browsers connect to the web app’s hostname on port 443 unless told otherwise, so either:

  • give the Service the ingress controller’s IP address, so the same hostname reaches both, e.g. with MetalLB:

    gateway:
      webtransport:
        service:
          annotations:
            metallb.io/allow-shared-ip: <the ingress Service's sharing key>
            metallb.io/loadBalancerIPs: <the ingress Service's IP>
    
  • or give it an address of its own and tell browsers where it is:

    gateway:
      webtransport:
        publicAddress: quake-webtransport.example.com:443
    

The gateway uses a self-signed certificate for WebTransport, which browsers accept by its hash, so it doesn’t need one from a CA. With more than one gateway replica, set gateway.webtransport.tls.secretName to a certificate browsers trust, since each replica would otherwise have its own.

By default, anyone who can reach the web app can create up to gateway.maxServers servers, and delete the ones created there. To sign people in, e.g. with Google Cloud’s Identity-Aware Proxy, and give servers owners, see Authentication. Setting gateway.maxServers to 0 turns creating servers off.

The chart can also route to the web app with Gateway API instead of an ingress, with gateway.httpRoute.

Create servers with kubectl

Servers can also be created as QuakeServer objects, in any namespace:

apiVersion: quake.kube/v1
kind: QuakeServer
metadata:
  name: deathmatch
spec:
  bot:
    minPlayers: 4
  game:
    type: FreeForAll
  maps:
    - name: q3dm17
    - name: q3dm7
kubectl apply -f deathmatch.yaml
kubectl get quakeservers

See Configuration for the rest of the spec, and Add Mods And Maps for running mods. Mods’ content images need Kubernetes 1.35, or 1.33 with the ImageVolume feature gate.

Uninstall

helm uninstall quake-kube --namespace quake-kube

Servers created from the web app are deleted with it (gateway.deleteServersOnUninstall). Ones created with kubectl are left running, along with the QuakeServer CRD, which is kept by default (crds.keep). Delete the CRD to remove them too:

kubectl delete crd quakeservers.quake.kube