A Kubernetes Operator for managing Quake 3 game servers. Built with kubebuilder, this operator provides a declarative way to deploy and manage Quake 3 servers in Kubernetes clusters.
- Declarative Server Management: Define Quake servers as Kubernetes resources
- Reusable Templates: Create
QuakeServerTemplateresources for shared configurations - Gateway API Integration: Expose servers via HTTPRoute (browser) and UDPRoute (native clients)
- Validation Webhooks: Automatic validation with clear error messages
- Secure Defaults: Non-root containers, dropped capabilities, read-only filesystem
- High Availability: Leader election support for multi-replica deployments
- Pause/Resume: Control server state via annotation
- Kubernetes v1.25+
- cert-manager (for webhook TLS)
- Gateway API CRDs (optional, for Gateway features)
# Install with default values
helm install quake-operator ../helm/quake-kube-operator \
-n quake-system --create-namespace
# Install with custom values
helm install quake-operator ../helm/quake-kube-operator \
-n quake-system --create-namespace \
--set replicaCount=2 \
--set image.tag=v0.1.0
# Disable webhooks (for development)
helm install quake-operator ../helm/quake-kube-operator \
-n quake-system --create-namespace \
--set webhook.enabled=false# Install CRDs
make install
# Run locally (outside cluster)
make run
# Deploy to cluster
make deploy IMG=<your-registry>/quake-operator:tagThe primary resource for deploying a Quake 3 server.
apiVersion: quakekube.io/v1alpha1
kind: QuakeServer
metadata:
name: my-server
spec:
# Required: Accept the Quake 3 EULA
agreeEula: true
# Optional: Reference a template for base configuration
templateRef:
name: ffa-standard
# Server configuration (overrides template values)
serverConfig:
fragLimit: 25
timeLimit: "15m"
game:
type: FreeForAll # FreeForAll, Tournament, TeamDeathmatch, CaptureTheFlag
motd: "Welcome!"
forceRespawn: false
inactivity: "10m"
quadFactor: 3
server:
hostname: "My Quake Server"
maxClients: 12
rconPassword: "secret"
bot:
minPlayers: 4 # Bots fill remaining slots
skill: 3 # 1-5 skill level
maps:
- name: q3dm7
- name: q3dm17
- name: q3tourney2
commands: # Extra server commands
- "set sv_allowDownload 1"
# Gateway API configuration
gateway:
enabled: true
gatewayRef:
name: main-gateway
namespace: gateway-system
hostname: "quake.example.com"
# Resource limits
resources:
limits:
cpu: "1"
memory: "512Mi"
requests:
cpu: "100m"
memory: "128Mi"Reusable configurations that can be referenced by multiple QuakeServer resources.
apiVersion: quakekube.io/v1alpha1
kind: QuakeServerTemplate
metadata:
name: ffa-standard
spec:
serverConfig:
fragLimit: 25
timeLimit: "15m"
game:
type: FreeForAll
motd: "Standard FFA Server"
server:
maxClients: 16
bot:
minPlayers: 4
skill: 3
maps:
- name: q3dm7
- name: q3dm17Templates support all the same serverConfig fields as QuakeServer. When a QuakeServer references a template, inline values override template values (strategic merge).
# List all servers
kubectl get quakeservers
# Get detailed status
kubectl describe quakeserver my-server
# View server events
kubectl get events --field-selector involvedObject.kind=QuakeServerPause a server (scales deployment to 0 replicas):
# Pause
kubectl annotate quakeserver my-server quakekube.io/paused=true
# Resume
kubectl annotate quakeserver my-server quakekube.io/paused-# List templates
kubectl get quakeservertemplates
# View which servers use a template
kubectl describe quakeservertemplate ffa-standard
# Status.usedBy shows dependent servers| Type | Description |
|---|---|
FreeForAll |
Every player for themselves |
Tournament |
1v1 duel mode |
TeamDeathmatch |
Team-based deathmatch |
CaptureTheFlag |
Capture the enemy flag |
SinglePlayer |
Single player mode |
The operator creates the following resources for each QuakeServer:
- Deployment: Runs the game server with a content-server sidecar
- ConfigMap: Server configuration (
config.yaml) - Service: ClusterIP with ports 8080 (HTTP), 27960 (UDP), 9090 (content)
- HTTPRoute (optional): Browser WebSocket access via Gateway API
- UDPRoute (optional): Native client access via Gateway API
By default, containers run with:
runAsNonRoot: truerunAsUser: 1000allowPrivilegeEscalation: falsereadOnlyRootFilesystem: truecapabilities.drop: ["ALL"]
These can be customized via spec.securityContext and spec.podSecurityContext.
# Unit tests
make test
# With coverage
go test -v -coverprofile=cover.out ./...
go tool cover -html=cover.out# Regenerate CRDs after modifying types
make manifests
# Regenerate deepcopy functions
make generategolangci-lint run# Using Helm
helm uninstall quake-operator -n quake-system
# Using Make
make undeploy
make uninstall # Removes CRDsCopyright 2026. Licensed under the Apache License, Version 2.0.