- Clone this repository:
git clone https://github.com/humanstew/ioq3-server.git cd ioq3-server - Add your Quake 3 game data:
- Place your
pak0.pk3(and optionally other .pk3 files) in thebaseq3/directory. - For Team Arena, add .pk3 files to
missionpack/. ⚠️ pak0.pk3 is not included in this repository due to copyright. You must provide your own..gitignoreis set to exclude pak0.pk3 files for safety.- Optional: drop non-standard/custom maps into both
baseq3/(so the server can load them) andfastdl/public/baseq3/(so clients can download them). Team Arena customs go into the matchingmissionpack/folders.
- Build and launch multiple servers:
docker compose up --build
- This will start three server instances (see
docker-compose.yml).
- This will start three server instances (see
- Each instance uses its own config and port (27960, 27961, 27962) plus:
fastdlon port 8080 for HTTP map downloads.landingon port 8081 serving an old-school server status page.
- Configs: Place all your server configs (e.g.
server-ffa.cfg,server-ctf.cfg,server-mp.cfg) in theconfigs/directory. Each server instance uses its own config via theSERVER_ARGSenvironment variable. - Environment Variables:
SERVER_ARGS(e.g.+exec server-ffa.cfg)SERVER_MOTD(server message)ADMIN_PASSWORD(RCON password, auto-generated if unset)FASTDL_URL(public HTTP(S) base path for map downloads; defaults tohttp://localhost:8080indocker-compose.yml)
- Volumes:
- Game data in
baseq3/is persisted and shared between instances (mounted read/write so configs can be copied at startup). - Configs in
configs/are editable and copied into the game directory at startup. - Fast-download assets live in
fastdl/public/and are served read-only by Nginx. Only place files there that you want the public to fetch (avoid official pak0.pk3).
- Game data in
services:
quake1:
build: .
image: ioq3-server
container_name: quake1
ports:
- "27960:27960/udp"
volumes:
- ./baseq3:/opt/quake3/baseq3
- ./configs:/opt/quake3/configs
environment:
- SERVER_ARGS=+exec server-ffa.cfg
quake2:
build: .
image: ioq3-server
container_name: quake2
ports:
- "27961:27960/udp"
volumes:
- ./baseq3:/opt/quake3/baseq3
- ./configs:/opt/quake3/configs
environment:
- SERVER_ARGS=+exec server-ctf.cfg
quake3:
build: .
image: ioq3-server
container_name: quake3
ports:
- "27962:27960/udp"
volumes:
- ./baseq3:/opt/quake3/baseq3
- ./configs:/opt/quake3/configs
environment:
- SERVER_ARGS=+exec server-mp.cfg
- FASTDL_URL=http://localhost:8080
fastdl:
image: nginx:1.27-alpine
container_name: fastdl
ports:
- "8080:80"
volumes:
- ./fastdl/nginx.conf:/etc/nginx/conf.d/default.conf:ro
- ./fastdl/public:/usr/share/nginx/html:rofastdlis an Nginx container that serves the contents ofbaseq3/andmissionpack/over HTTPS (via Caddy) with directory listings enabled.- Accessible via two methods:
- Path-based (default):
https://yourdomain.com/fastdl - Subdomain: Set
FASTDL_SUBDOMAIN=cdn.yourdomain.comforhttps://cdn.yourdomain.com
- Path-based (default):
- Update
FASTDL_URLto match your chosen method so clients can fetch custom maps quickly. - SSL certificates are automatically managed by Caddy (Let's Encrypt).
- Files remain read-only inside the container, preventing accidental deletion.
- Only copy non-standard maps into
fastdl/public/baseq3orfastdl/public/missionpackso official assets likepak0.pk3are never exposed publicly.
landingis a Node/Express app that queries each server via UDP and renders a 90s-style table showing online/offline state, map, and player counts.- Configure displayed servers via the
SERVERS_JSONenvironment variable indocker-compose.yml(defaults target the bundledquake1-3services). - Access via HTTPS at your domain (e.g.,
https://quake.example.com).
-
Both the landing page and fastdl are served through Caddy, which automatically obtains and renews Let's Encrypt SSL certificates.
-
Requirements:
- Set the
DOMAINenvironment variable to your fully qualified domain name (e.g.,quake.example.com) - Ensure ports 80 and 443 are accessible from the internet
- Your DNS must point to your server's public IP
- (Optional) Set
FASTDL_SUBDOMAINif you want fastdl on a separate subdomain
- Set the
-
Example
.envconfigurations:Path-based fastdl (default):
DOMAIN=quake.example.com FASTDL_URL=https://quake.example.com/fastdl
Subdomain-based fastdl:
DOMAIN=quake.example.com FASTDL_SUBDOMAIN=cdn.quake.example.com FASTDL_URL=https://cdn.quake.example.com
-
Caddy stores certificates in Docker volumes (
caddy-dataandcaddy-config) for persistence across restarts. -
For development/testing without a domain, Caddy will use a self-signed certificate when
DOMAIN=localhost.
- Runs as non-root user (
ioq3ded) inside the container. - Only the dedicated server binary is included in the final image.
- Change ioquake3 version:
docker build --build-arg IOQUAKE3_COMMIT=release-1.36 -t ioq3-server . - Custom configs: Mount your own config directory or edit
files/default-configs/. - Multiple servers: Add more services in
docker-compose.ymlas needed.
Dockerfile— Automated build, fetches ioquake3 source from GitHubdocker-compose.yml— Multi-instance orchestrationfiles/entrypoint.sh— Startup logic, config managementbaseq3/,missionpack/— Game assets (pak files, not included)configs/— All server configs (editable, copied into baseq3 at startup)files/default-configs/— Default server configs
- Q: Why do I need to provide
pak0.pk3?- A: Due to licensing, you must supply your own Quake 3 data files. pak0.pk3 is not included in this repository and must be added manually.
- Q: How do I connect?
- A: Use your server's IP and the mapped port (e.g.,
quake3://your-ip:27960).
- A: Use your server's IP and the mapped port (e.g.,
- Q: How do I set the RCON password?
- A: Set the
ADMIN_PASSWORDenvironment variable or let it auto-generate.
- A: Set the
- This project automates ioquake3 server deployment. Quake 3 data files are not included.
- See ioquake3 license for engine details.
Made with ❤️ for easy Quake 3 server hosting. Frag on!