⚠️ Work in Progress: This project is functional but still in active development. While the server runs successfully and has been tested with real gameplay, there may be edge cases or configuration options that haven't been thoroughly tested. Contributions and issue reports are welcome!
This flake packages the Unreal Tournament 2004 dedicated server and provides a NixOS module for easy deployment.
- UT2004 dedicated server package (version 3369.3 with bonus pack)
- NixOS module with systemd service
- Configurable game types, maps, and server settings
- Automatic firewall configuration
- Security hardening with systemd
- Pre-configured with OpenSpy master server (Epic Games servers are offline)
Build and run the server directly:
# Build the package
nix build github:DenzoNL/nix-ut2004
# Run the server binary
nix run github:DenzoNL/nix-ut2004Add this flake to your NixOS configuration:
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11";
ut2004-server.url = "github:DenzoNL/nix-ut2004";
};
outputs = { self, nixpkgs, ut2004-server }: {
nixosConfigurations.your-hostname = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
./configuration.nix
# The module automatically sets up the package overlay
ut2004-server.nixosModules.default
];
};
};
}The module automatically makes the ut2004-server package available - no need to manually configure overlays!
All server configuration is done through the settings attribute set, which provides a fully declarative way to configure UT2004.ini. The attribute set uses section names as keys (e.g., "Engine.GameReplicationInfo") with nested key-value pairs for each setting.
Default values are provided for basic server identity, so you only need to override what you want to customize.
{
services.ut2004-server = {
enable = true;
gameType = "XGame.xDeathMatch";
map = "DM-Rankin";
maxPlayers = 16;
openFirewall = true;
adminPasswordFile = "/run/secrets/ut2004-admin-password";
settings = {
"Engine.GameReplicationInfo" = {
ServerName = "My UT2004 Server";
AdminName = "Admin";
AdminEmail = "admin@example.com";
};
};
};
}{
services.ut2004-server = {
enable = true;
gameType = "XGame.xCTFGame";
map = "CTF-FaceClassic";
maxPlayers = 20;
port = 7777;
queryPort = 7778;
webAdminPort = 8080;
openFirewall = true;
settings = {
"Engine.GameReplicationInfo" = {
ServerName = "CTF Server";
MessageOfTheDay = "Welcome to our CTF server!";
};
};
};
}{
services.ut2004-server = {
enable = true;
gameType = "XGame.xTeamGame";
map = "DM-Rankin";
maxPlayers = 16;
openFirewall = true;
adminPasswordFile = "/run/secrets/ut2004-admin-password";
settings = {
"Engine.GameReplicationInfo" = {
ServerName = "My Custom Server";
MessageOfTheDay = "Welcome! Have fun!";
};
"Engine.GameInfo" = {
GoalScore = 50; # First team to 50 wins
bAllowBehindView = false; # Disable third-person camera
};
};
};
}XGame.xDeathMatch- DeathmatchXGame.xTeamGame- Team DeathmatchXGame.xCTFGame- Capture the FlagXGame.xDoubleDom- Double DominationXGame.xBombingRun- Bombing RunOnslaught.ONSOnslaughtGame- Onslaught
enable- Enable the UT2004 server service
settings- Attribute set for UT2004.ini configuration. Each key is a section name (e.g., "Engine.GameReplicationInfo") and its value is an attribute set of key-value pairs. Common settings include:Engine.GameReplicationInfo.ServerName- Server name shown in browserEngine.GameReplicationInfo.AdminName- Administrator nameEngine.GameReplicationInfo.AdminEmail- Administrator emailEngine.GameReplicationInfo.MessageOfTheDay- Welcome messageIpDrv.MasterServerLink- Master server configuration (defaults to OpenSpy at utmaster.openspy.net:28902, as Epic's servers are no longer online)- See examples above for more options
gameType- Game mode to run (default: "XGame.xDeathMatch")map- Starting map (default: "DM-Rankin")maxPlayers- Maximum players (default: 16)
port- Game port (default: 7777)queryPort- Query port (default: 7778)webAdminPort- Web admin port (default: 8080)openFirewall- Open game and query ports in firewall (default: false)openWebAdmin- Open web admin port in firewall (default: false)
adminPassword- Admin password (stored in Nix store - not recommended)adminPasswordFile- Path to file containing admin password (recommended)
stateDir- State directory (default: "/var/lib/ut2004-server")user- Service user (default: "ut2004")group- Service group (default: "ut2004")package- Override the UT2004 package
The module provides separate firewall controls for game and admin interfaces:
Game Ports (openFirewall = true):
- UDP:
port(default 7777) - Game traffic - UDP:
queryPort(default 7778) - Server browser queries
Web Admin (openWebAdmin = true):
- TCP:
webAdminPort(default 8080) - Web administration interface
For a public server, you typically want openFirewall = true but keep openWebAdmin = false (access web admin via SSH tunnel or local network only).
- Admin Password: Use
adminPasswordFileinstead ofadminPasswordto avoid storing credentials in the Nix store - Game Firewall: Only enable
openFirewallif you intend to run a public server - Web Admin Firewall: Keep
openWebAdmin = falseunless you need remote web administration. The web admin interface provides full server control and should be protected (use SSH tunnels or restrict to local network) - Web Admin Password: Always set a strong admin password when the web admin interface is enabled
# Build the package
nix build .#ut2004-server
# Run the automated tests
nix build .#checks.x86_64-linux.ut2004-server
# or simply
nix flake check
# Enter development shell
nix develop
# Test the NixOS module in a real system
nixos-rebuild test --flake .#your-hostnameThe project includes comprehensive tests:
# Run all checks (automated tests)
nix flake check
# Run just the server integration test
nix build .#checks.x86_64-linux.ut2004-server
# Run config generation unit tests
nix build .#checks.x86_64-linux.config-generationAutomated tests verify:
- Service starts successfully
- Process is running
- Ports are listening (7777 UDP for game, 7778 UDP for queries)
- State directory is created with correct permissions
- Server logs are being written
- Service can be stopped and restarted cleanly
- Configuration generation is correct (14 unit tests)
To manually test and connect to the server with a UT2004 client:
# Build the interactive test VM
nix build .#checks.x86_64-linux.ut2004-server-interactive.driverInteractive -o interactive
# Start the test VM
./interactive/bin/nixos-test-driverInside the interactive test:
- The Python REPL starts automatically
- Type
start_all()to start the VM and server - Server will be available at
localhost:7777 - Connect with your UT2004 client to test gameplay
Useful commands inside the REPL:
# View server logs
machine.succeed("tail -n 50 /var/lib/ut2004-server/System/server.log")
# Check generated configuration
machine.succeed("cat /var/lib/ut2004-server/System/UT2004.ini | head -100")
# Check service status
machine.succeed("systemctl status ut2004-server.service")
# Exit when done
exit()Connection details:
- Address:
localhostor127.0.0.1 - Port:
7777 - Map:
DM-Rankin - Game Type: Deathmatch
- Admin Password:
admin123
Note: The test VM requires at least 32GB of disk space. This accounts for:
- UT2004 files in Nix store: 2.7GB
- Copy of files in state directory: 2.7GB
- NixOS base system: ~5GB
- Overhead and temp files: ~3GB
The tests are configured with 32GB automatically.
- UT2004 client package (when freely distributed)
- Additional mutators and mods support
- Automated map rotation configuration
- Server statistics and monitoring integration
This Nix packaging code is licensed under the MIT License - see the LICENSE file for details.
Important: The Unreal Tournament 2004 server binaries are proprietary software owned by Epic Games. This project only provides packaging and deployment tools for existing UT2004 installations.
Contributions welcome! Please open an issue or pull request.