• About Me
  • Impressum
  • Privacy
  • Fun Stuff

№42

... it's better to have good questions

ioquake3 in a container

January 6, 2022 5 min read Technology Games How-To Ronny Trommer

Once upon a time, people had no internet access, or it was very expensive and slow. To have some fun, they spent weekends with their friends and hung out playing games over a local area network. It was so much fun it gained some interest and the space from your friends house was just too small for all the people. Parents and families went crazy and electric bills went through the roof. Locations got bigger and peoples needed a bit more advanced networks and dedicated servers. You started writing your first programs managing tournaments and automating dedicated servers. … it was long before we talked about something like Ansible or Salt Stack :)

I had some flashbacks, when I ran into the ioquake3 project. I’ve played Q3 a lot and I also ran a some infrastructure back in the days. When id software open sourced the id Tech 3 engine, a few people made it possible to compile and run it on modern hardware - which is really amazing! I’ve spent some time and built a container image to run a ioquake3 dedicated server with something like Docker. Additionally, I’ve found a pretty old mercurial repository which allows you to run also a master server as well.

Here is how you run your own slaughterhouse, and hopefully you have some similar flashbacks like me when you write your server configs :) What you absolutely need is an original Quake 3 Arena with the pak files. They have the original game assets which are not part of the open sourced id Tech 3 engine.

Build a ioquake3 container image for a dedicated server

We build from the main branch from ioquake3 and compile it in a slim Alpine 3.15.0 base image. I use multi-stage build to have a very minimal ioquake3 image as a result.

######
## Builder stage: Compiling from source
####
FROM "alpine:3.15.0" as builder

USER root

WORKDIR /root

RUN apk --no-cache add curl g++ gcc make git sdl2-dev && \
    git clone https://github.com/ioquake/ioq3.git && \
    cd ioq3 && make release

######
## Assemble stage: Create a minimal image as runnable artifact
####
FROM "alpine:3.15.0"

RUN adduser --system ioq3

COPY --chown=ioq3 --from=builder /root/ioq3/build/release-linux-x86_64 /opt/ioq3

USER ioq3

ENTRYPOINT [ "/opt/ioq3/ioq3ded.x86_64" ]

CMD [ "-v" ]

### Runtime information and not relevant at build time

VOLUME [ "/opt/ioq3/baseq3" ]

EXPOSE 27960/udp

LABEL org.opencontainers.image.source="https://github.com/labmonkeys-space/game-container.git" \
      org.opencontainers.image.revision="8fd6d17" \
      org.opencontainers.image.vendor="Labmonkeys Space" \
      org.opencontainers.image.authors="ronny@no42.org" \
      org.opencontainers.image.licenses="MIT"

If you are impatient, I have the build instructions in my GitHub repo, you can just fork it. The OCI is published with CircleCI into my Labmonkeys organization on Quay.io if you want to use them right away.

Set up and run a dedicated ioquake3 server

To run a dedicated server you can use the following docker-compose.yml as a starting point. I use a bind mount directory for the baseq3 directory. It should contain all pak files from the Quake 3 Arena CD and your server.cfg file. The parameter dedicated 2 promotes your server to the master server specified in your server.cfg. Set it to dedicated 1 if you don’t want to publish your server to master servers. The default port is 27960/udp.

Pro-tip: You can use different ports with +set net_port 27961 which allows you to run multiple server instances with different game types on the same machine.

---
version: '3'

services:
  ioq3:
    image: quay.io/labmonkeys/ioquake3:20211223.b3
    container_name: ioq3
    deploy:
      resources:
        limits:
          cpus: '1.0'
          memory: 128M
    command: [ "+set", "dedicated", "2", "+exec", "server.cfg" ]
    volumes:
      - "./baseq3:/opt/ioq3/baseq3"
    ports:
      - "27960:27960/udp"

Just as an example, I had set resource limits on the process preventing it to eat all my resources. I have found a good article for a server.cfg in a blog post from Sebastian on technik.blogbasis.net. Helped a lot and kudos, Sebastian :)

// ************************************
// FILENAME: server.cfg
// FILEPATH: /usr/games/ioquake3/baseq3
// FILETYPE Server Configuration File
// GAMETYPE "Team Deathmatch"
// ************************************
// "//" als kommentarzeichen
// multikern-prozessor?
// 0 = nein 1 = ja
seta r_smp "1"
// Spieltyp
// 0 = "Free for All"
// 1 = "Tournament"
// 2 = "Free for All" (yes, it's listed again)
// 3 = "Team Deathmatch"
// 4 = "Capture The Flag"
g_gametype 3
// sollen bots mitspielen?
// 0 = nein 1 = ja
bot_enable "1"
// minimale spieleranzahl (wird durch bots aufgefüllt)
bot_minplayers "10"
// wie oft muss in ctf die flagge erobert werden?
capturelimit 10
// fraglimit
fraglimit 50
// zeitlimit
timelimit 15
// maximale spieleranzahl (zuschauer zählen mit)
sv_maxClients 32
// servername
sv_hostname "Gehaxelts Ioquake Server"
// message of the day
g_motd "Happy Fragging!"
// downloads erlauben?
// 0 = nein 1 = ja
sv_allowdownload "1"
// 0 = nein 1 = ja
sv_floodProtect "1"
// passwortschutz
// 0 = nein 1 = ja
sv_privateClients 0
// passwort
// "" = kein passwort
sv_privatePassword ""
// admin passwort
rconPassword "ChangeMe!"
// pure-server?
// 0 = nein 1 = ja
sv_pure 1
// maximale bandbreite für einen spieler in bytes
sv_maxRate 10000
// masterserver
sv_master2 ""
sv_master3 ""
sv_master4 ""
sv_master5 ""
// level-auswahl
set d1 "map q3dm1 ; set nextmap vstr d2"
set d2 "map q3dm2 ; set nextmap vstr d3"
set d3 "map q3dm3 ; set nextmap vstr d4"
set d4 "map q3dm4 ; set nextmap vstr d5"
set d5 "map q3dm5 ; set nextmap vstr d6"
set d6 "map q3dm6 ; set nextmap vstr d7"
set d7 "map q3dm7 ; set nextmap vstr d8"
set d8 "map q3dm8 ; set nextmap vstr d9"
set d9 "map q3dm9 ; set nextmap vstr d10"
set d10 "map q3dm10 ; set nextmap vstr d11"
set d10 "map q3dm11 ; set nextmap vstr d12"
vstr d1
// waffen-respawn-zeit
g_weaponrespawn 5
// votings erlauben?
// 0 = nein 1 = ja
g_allowvote 0
// schadensvervielfachung quaddamage
g_quadfactor 4
// demo aufzeichnung erlauben?
// 0 = nein 1 = ja
g_syncronousClients 1
// zusätzliche angaben
sets "Administrator" "admin"
sets "Email" "accs@gehaxelt.in"
sets "URL" "http://gehaxelt.in"
sets "Location" "Germany"
sets "CPU" "CPU"
// sets "mappack" "http://www.myserver.com/mappack.zip"
view raw server.cfg hosted with ❤ by GitHub

You can run the service in background with docker-compose up -d.

Build and run your own master server

If you want to provide your own master server which allows people to find games you need dpmaster. I’ve forked it to GitHub and here are the instructions to build a OCI for it from source same procedure:

######
## Builder stage: Compiling from source
####
FROM "alpine:3.15.0" as builder

USER root

WORKDIR /root

RUN apk --no-cache add curl g++ gcc make git && \
    git clone https://github.com/labmonkeys-space/dpmaster && \
    cd dpmaster/src && make release

######
## Assemble stage: Create a minimal image as runnable artifact
####
FROM "alpine:3.15.0"

RUN adduser --system dpmaster

COPY --chown=dpmaster --from=builder /root/dpmaster/src/dpmaster /usr/local/bin

USER dpmaster

ENTRYPOINT [ "/usr/local/bin/dpmaster" ]

CMD [ "--help" ]

### Runtime information and not relevant at build time

EXPOSE 27950/udp

LABEL org.opencontainers.image.source="https://github.com/labmonkeys-space/game-container.git" \
      org.opencontainers.image.revision="daf6272" \
      org.opencontainers.image.vendor="Labmonkeys Space" \
      org.opencontainers.image.authors="ronny@no42.org" \
      org.opencontainers.image.licenses="MIT"

You find the same OCI image on my Labmonkeys organization on Quay.io as well if you want to give it a spin. The docker-compose.yml for the service is pretty straight forward.

---
version: '3'
  
services:
  dpmaster:
    image: quay.io/labmonkeys/dpmaster:2.3-dev.b10
    container_name: dpmaster
    command: [ "-f", "-v"  ]
    ports:
      - "27950:27950/udp"

The master server is listening by default on port 27950/udp. Start your service in background with docker-compose up -d. If you want to promote your dedicated server just add in your master server in your server.cfg to something like sv_master3 "<your-master-server.host>", +set dedicated 2 you are ready to go.

As soon your dedicated server started you will see in your dpmaster log docker-compose logs something like this:

dpmaster    | * 2022-01-06 22:47:53 UTC
dpmaster    | > New packet received from 192.168.32.1:57963: "\xFF\xFF\xFF\xFFheartbeat QuakeArena-1\x0A" (27 bytes)
dpmaster    | > 192.168.32.1:57963 ---> heartbeat (QuakeArena-1)
dpmaster    |   - belongs to game "Quake3Arena"
dpmaster    | > New server added: 192.168.32.1:57963. 2 server(s) now registered, including 1 for this address quota
dpmaster    |   - index: 1
dpmaster    |   - hash: 0x01CA
dpmaster    | > 192.168.32.1:57963 <--- getinfo with challenge "P)QT^z.qmvC"

You are now ready to build your own little gaming community and wish you all gl & hf!

Fun Dedicated Server Lan Parties Quake-3-Arene
Latest posts
  • Streaming telemetry with gNMI
  • Go away or I replace you with a Makefile
  • SNMP Proxy ... wait what?
  • Hackathon on BGP monitoring using BMP in OpenNMS
  • Dealing with secrets in OpenNMS Horizon
  • Building container images for OpenNMS
  • Hello Containerlab with Orbstack
  • OpenNMS Horizon with RRDtool
  • Demystifying iplike in OpenNMS Horizon
  • JniPing vs. JnaPing
Categories
  • OpenNMS (27)
  • Technology (24)
  • How-To (21)
  • Monitoring (13)
  • Tutorial (8)
  • Events (6)
  • Open-Source (6)
  • Container (5)
  • BGP (2)
  • Containers (2)
Social media
© 2025 made in 🇪🇺 by Ronny Trommer with ❤️ for Open Source
Bilberry Hugo Theme