Why Linux for a Game Server in the First Place?

The default choice for a hobbyist game server is often Windows -- it is what the game client runs on, it is familiar, and many tutorials assume it. But when you actually stand up a dedicated server meant to run uninterrupted for weeks, host multiple concurrent players, and support a stack of mods, Linux starts looking considerably more attractive.

Linux offers lower baseline memory consumption, no background Windows Update restarts interrupting your session, fine-grained process control with tools like systemd and tmux, and mature scripting infrastructure for automating updates and backups. A clean Ubuntu Server 24.04 installation idles at roughly 200-300 MB of RAM, while Windows Server 2022 idles at 1.5-2 GB before any services are added -- on a 4 GB VPS, that difference is the margin between running one game server comfortably and running out of memory under load. LinuxGSM, the widely used command-line management tool for Linux dedicated servers, has been steadily growing with new server support since 2012 and now covers 139 individual game server types as of early 2026. The project's creator Daniel Gibbs describes it as "a command-line tool designed to be as simple as possible." For anyone comfortable at a terminal, Linux is simply the better host.

There is another advantage that is less discussed but operationally significant: Linux gives you full control over when and how updates happen. On Windows, a game server host has to contend with Windows Update, Defender definition updates, and .NET runtime updates that can restart services or consume disk I/O at unpredictable times. On Linux, apt and dnf only update when you tell them to, and unattended-upgrades (if enabled) can be configured to apply security patches without rebooting. For a game server that needs to be available during peak hours, this predictability is not a luxury -- it is a baseline requirement.

The catch is that modding on Linux -- especially for Unity-based games -- has historically required more manual effort than on Windows. That gap has narrowed substantially in recent years as tools have matured, and by 2026 the Linux modding ecosystem is more capable than ever. But the nuances are still worth understanding before you jump in. This guide covers the full picture: from standing up SteamCMD correctly to running a modded server as a proper systemd service with automated restarts and update hooks.

Scope

This guide focuses on self-hosted dedicated servers running a native Linux binary or a Steam-sourced server image. It is not primarily about running client-side mods under Proton, though some BepInEx notes apply to both contexts.

Which Distro? Keep It Simple

For a headless game server, the distro choice is less important than many guides make it sound. You do not need a "gaming distro" -- those are built for desktop gaming with GPU drivers, Proton, and compositor tuning, none of which matter for a headless server. What you need is a stable, well-supported server distribution with long-term security updates and a package manager that SteamCMD and LinuxGSM work with out of the box. In practice, that means Ubuntu Server 24.04 LTS or Debian 12 (Bookworm). Ubuntu LTS receives five years of standard support (extended to ten with Ubuntu Pro), has the largest community and the widest tutorial coverage for game server hosting, and is the distribution that LinuxGSM, Pterodactyl, and nearly every hosting provider's one-click templates are built and tested on. Debian is leaner -- it idles at under 100 MB of RAM on a minimal install, compared to Ubuntu Server's roughly 200-300 MB -- and is the right choice if you want absolute stability with no surprises, especially on a low-spec VPS. Rocky Linux 9 or AlmaLinux 9 are solid alternatives if your organization standardizes on RHEL-family distributions, but be aware that SteamCMD installation requires extra steps on RHEL-based systems (manual tarball download instead of a package manager install) and LinuxGSM's dependency lists assume Debian or Ubuntu by default. Avoid rolling-release distributions (Arch, Manjaro, Fedora) for production game servers -- a kernel or glibc update that breaks a game binary at 2 AM on a Saturday is not something you want to troubleshoot while your players wait. One technical constraint worth noting: many game servers have minimum glibc requirements -- Valheim, for example, requires GLIBC 2.29 and GLIBCXX 3.4.26, as documented in Iron Gate's official guide. Debian 10 (Buster) and Ubuntu 20.04 meet this threshold; older LTS versions do not. If you are running an older distribution and cannot upgrade, Docker is the recommended workaround for running a game binary that requires a newer glibc.

Step Zero: Never Run as Root

Before anything else is installed, establish a dedicated non-root system user. Game server software is third-party code you do not control. Mods are even further removed from any security review. Running either as root exposes your entire system if something misbehaves or gets compromised. The Valve Developer Community SteamCMD documentation explicitly warns against running SteamCMD as the root user, calling it a security risk. This is not theoretical -- game server binaries execute arbitrary code downloaded from Valve's servers, and mods execute arbitrary code downloaded from community repositories with no code review process. A compromised mod running as root has unrestricted access to your entire system, including other services, SSH keys, and any data stored on the host.

bash -- create a dedicated server user
$ sudo useradd -m -s /bin/bash steam  # -m creates a home directory; -s sets the login shell
$ sudo passwd steam          # set a strong password
$ sudo su - steam           # switch to the steam user for all further setup

Keep this user separate from your own login. The user named steam is conventional, but anything sensible works. What matters is isolation: the game server process, SteamCMD, mod files, and all related scripts should live under this account's home directory and run as this account's process. The reason this isolation matters beyond security is practical: if the game server fills its home directory with crash dumps or excessive log files, it will not affect your own login session or other services. If a mod writes files with unexpected permissions, it does so within the sandbox of the steam user's home, not across your whole filesystem. And if you ever need to blow away a broken server install and start fresh, you can wipe the steam user's home directory without affecting anything else on the system.

SteamCMD: The Foundation for Steam-Based Games

SteamCMD is the command-line version of the Steam client, designed specifically for server administrators. Its primary function is installing and updating dedicated server files and, in some configurations, Steam Workshop content. Many major multiplayer games on Steam -- Counter-Strike 2, Garry's Mod, Rust, ARK, Left 4 Dead 2, and dozens of others -- distribute their dedicated server binaries through SteamCMD.

Because SteamCMD is a 32-bit application -- Valve has never shipped a native 64-bit build -- you need to add i386 architecture support on 64-bit Ubuntu or Debian systems before installing it. Without this, the package manager will refuse to install SteamCMD because its 32-bit binary dependencies are not resolvable on a pure 64-bit system. Some server setups also require lib32stdc++6 for C++ standard library support needed by game server binaries that link against 32-bit libraries:

bash -- Ubuntu/Debian SteamCMD install
$ sudo add-apt-repository multiverse  # enables non-free packages; SteamCMD is in multiverse because it is proprietary Valve software
$ sudo dpkg --add-architecture i386  # required because SteamCMD is a 32-bit binary with no 64-bit version available
$ sudo apt update
$ sudo apt install lib32gcc-s1 steamcmd
Debian vs. Ubuntu Repository Difference

On Debian systems, the add-apt-repository multiverse command does not apply -- Debian uses non-free and contrib repository components rather than Ubuntu's multiverse. On Debian, you would instead edit /etc/apt/sources.list to add non-free to your existing repository lines, or install SteamCMD manually by downloading it directly from Valve. The dpkg --add-architecture i386 step is the same on both distributions.

Once installed, SteamCMD is invoked interactively or via scripted one-liners. The core pattern for downloading a game server is to log in (anonymously for most dedicated server apps), set the install directory, then call app_update with the game's AppID and the validate flag to verify file integrity:

bash -- download a server (Garry's Mod, AppID 4020)
$ steamcmd \
  +force_install_dir /home/steam/gmod \
  +login anonymous \
  +app_update 4020 validate \
  +quit
Command Order Matters: force_install_dir Before login

Valve's SteamCMD now emits a warning if +force_install_dir is placed after +login. The correct order is +force_install_dir first, then +login, then +app_update. Many older guides and scripts -- including guides that were correct when they were written -- still use the old order (+login first), and you will see this warning in your logs: Please use force_install_dir before logon!. The download still succeeds with the old order for now, but Valve recommends the new order, and future SteamCMD updates could enforce it. Every SteamCMD command in this guide uses the correct order.

The validate argument tells SteamCMD to checksum every file against Valve's servers and redownload anything that does not match. This is more expensive on repeated runs but is worth including whenever you are troubleshooting a broken install. For day-to-day updates you can drop it to speed up the process. A note on this: there is some confusion online about whether validate should always be included. Some guides include it in every automated update script; others warn that it will overwrite custom configuration files. The truth is nuanced -- validate only replaces files that shipped with the server install and does not touch files you have added (like mods or custom configs in separate directories). However, as the LinuxGSM documentation explicitly notes, it will overwrite customised default files such as mapcycle.txt or server.cfg if you have edited the originals in place rather than using a separate override mechanism. We recommend using validate for initial installs and troubleshooting, and omitting it for routine scheduled updates to avoid accidentally reverting deliberate file changes.

Game AppID vs. Server AppID: A Common and Costly Mistake

Many games use a different Steam AppID for the dedicated server than for the game client itself. Valheim the game is AppID 892970; the Valheim Dedicated Server is AppID 896660. Using the wrong AppID is one of the most frequent SteamCMD errors: if you try to download AppID 892970 with +login anonymous, SteamCMD will return No subscription because the game client requires ownership verification -- only the dedicated server AppID (896660) supports anonymous login. Similarly, 7 Days to Die uses 251570 for the game and 294420 for the server. If you get a "No subscription" error and you know you are logging in anonymously, the first thing to check is whether you are using the game's AppID instead of the server's AppID. Look up the correct server AppID on SteamDB or the Valve Dedicated Servers List before starting. Additionally, SteamCMD prints passwords in plaintext in both the terminal and log files -- if you are using an authenticated login for a game that requires it, be aware that anyone with read access to the server's log directory can see your Steam password. The LinuxGSM SteamCMD documentation explicitly warns about this. Use a dedicated Steam account for your server, never your personal gaming account.

Scripting Updates

Rather than typing that command manually every time a game patches, wrap it in a shell script and wire it to a cron job or systemd timer. Here is a minimal update script that can be reused across different server installs by parameterizing the AppID and install directory:

update-server.sh
#!/bin/bash
set -e  # Exit immediately if any command fails

# Reusable SteamCMD update wrapper

STEAM_CMD="/usr/games/steamcmd"
INSTALL_DIR="/home/steam/gmod"
APP_ID="4020"

"${STEAM_CMD}" \
  +force_install_dir "${INSTALL_DIR}" \
  +login anonymous \
  +app_update "${APP_ID}" \
  +quit
# Add 'validate' after the APP_ID above only when troubleshooting a broken install

Schedule this with a cron entry under the steam user (not root). A 3 AM daily update is a common choice for servers that see heavy daytime traffic:

$ 0 3 * * * /home/steam/scripts/update-server.sh >> /home/steam/logs/update.log 2>&1
Version Mismatch Risk

If a game patches on Steam but your mods have not been updated for the new version, running app_update will break the server. Test updates on a staging instance first, or pause automatic updates and review patch notes before applying them to production.

LinuxGSM: Orchestrating the Whole Stack

For administrators running more than one or two servers, or who want monitoring, automated restarts, and structured logging without writing everything from scratch, LinuxGSM is worth knowing. It is a Bash-based framework, MIT-licensed and actively maintained on GitHub by lead developer Daniel Gibbs, that wraps SteamCMD and provides a consistent management interface across 139 supported game servers as of early 2026.

"Admins often have to spend hours messing around trying to get their servers working. LinuxGSM is a command-line tool designed to be as simple as possible, allowing admins to spend less time on management and more time gaming." -- LinuxGSM official website, linuxgsm.com

Installing a Minecraft Java Edition server under LinuxGSM, for example, follows this pattern:

bash -- LinuxGSM Minecraft server install
$ curl -Lo linuxgsm.sh https://linuxgsm.sh
$ chmod +x linuxgsm.sh
$ bash linuxgsm.sh mcserver
$ ./mcserver install

LinuxGSM checks and installs dependencies automatically if the server user has sudo access, downloads the game server files via SteamCMD or direct download depending on the game, and drops default configuration files in place. From there, ./mcserver start, ./mcserver stop, ./mcserver update, and ./mcserver monitor give you a uniform interface regardless of which game you are running.

The monitor command is particularly useful: it queries the running server process and, if the server stops responding, triggers a restart. Combine this with a cron job and you have basic self-healing uptime without needing a full orchestration layer like Kubernetes for a personal server.

LinuxGSM and Modded Servers

LinuxGSM installs the vanilla server binary by default. For modded Minecraft specifically, you need to manually replace or supplement the server JAR with Forge, Fabric, or Paper after LinuxGSM has set up the directory structure. The framework's documentation explicitly notes this limitation and points to PaperMC and Waterfall variants available as separate LinuxGSM server types.

The Docker Alternative: Pterodactyl and Containers

If you are managing more than two or three game servers, or if you want stronger isolation between server instances, Docker containers are worth considering as an alternative to running game servers directly on the host. Pterodactyl Panel is a free, open-source game server management panel that runs every game server inside its own isolated Docker container with enforced CPU, RAM, and disk limits. The Pterodactyl project describes its approach as running "all game servers in isolated Docker containers while exposing a beautiful and intuitive UI." It supports hundreds of games through community-maintained configuration templates called "eggs," and provides a web-based panel for file management, console access, backups, and user permissions. For multi-server operators, hosting communities, or anyone who wants to give friends admin access to their own server without giving them shell access to the host, Pterodactyl solves real problems that LinuxGSM does not. However, Pterodactyl adds significant infrastructure complexity -- it requires a web server, PHP, a database (MariaDB or MySQL), Redis, and a separate Wings daemon on each game server node. For a single server hosting one game for friends, this is overkill. Use LinuxGSM or a manual systemd setup instead. One caveat worth noting: Pterodactyl's development has slowed in recent years, and community forks have taken the lead. Pelican (AGPL-licensed, with a plugin system and active development) and Pyrodactyl (focused on performance, with significantly smaller bundle sizes and faster build times) are both more actively maintained as of 2026. If you are starting fresh, evaluate these forks before committing to the original Pterodactyl codebase. For Valheim specifically, the valheim-server-docker project by lloesche provides a well-maintained single-container solution with built-in BepInEx support, automatic updates, and systemd integration -- a simpler path than Pterodactyl for a single-game setup.

Docker vs. Bare Metal for Mods

Containerized game servers add a layer of isolation that is genuinely useful for security -- a compromised mod inside a Docker container cannot easily access the host filesystem or other containers. However, Docker also adds complexity to mod management: you need to mount mod directories as volumes, ensure file permissions align between the container user and the host, and rebuild or restart containers when updating mods. For BepInEx-based games, this means mounting the BepInEx/plugins/ and BepInEx/config/ directories as persistent volumes so that mod files survive container restarts. If you are comfortable with Docker, the isolation benefits are worth it. If you are not, a well-configured non-root user with a systemd service (as described in this guide) provides sufficient isolation for a personal server.

Steam Workshop Mods on a Headless Server

Games that use Steam Workshop for mod distribution -- Garry's Mod, ARK: Survival Evolved, Don't Starve Together, and others -- require a slightly different approach when running headless on Linux, since there is no Steam client graphical interface to click through.

For Source engine games like Garry's Mod, workshop content is fetched at server startup using a collection ID and a Steam Web API authorization key. You create a workshop collection on the Steam website, note its numeric ID from the URL, and pass it as a startup argument:

bash -- Garry's Mod with Workshop collection (using screen)
$ cd /home/steam/gmod
$ screen -A -m -d -S gmod \
  ./srcds_run \
  -console \
  -game garrysmod \
  +maxplayers 16 \
  +map gm_construct \
  +host_workshop_collection YOUR_COLLECTION_ID \
  -authkey YOUR_API_KEY

The API key is generated through the Steam Web API portal at steamcommunity.com/dev/apikey. Without it, workshop content cannot be downloaded at server startup. The reason Valve requires this key is that Workshop downloads go through the Steam Web API rather than through SteamCMD directly, and the API key authenticates your server as a legitimate requester. The key is tied to your Steam account, not to the game -- one key works for all Workshop-enabled games. Keep this key private; anyone with it can make API calls against your Steam account's rate limits.

For Don't Starve Together dedicated servers, the approach is different: you edit a Lua configuration file called dedicated_server_mods_setup.lua inside the mods/ directory of the server install. Each mod is declared with its Workshop ID using the ServerModSetup function. Mod updates on this platform require a full server restart to take effect -- the server does not hot-reload Lua mod files while running.

Client-Server Version Parity

When managing workshop mods across both a dedicated server and connected clients, version mismatches are a frequent source of failed connections. The Satisfactory modding documentation notes that separate profile management on client and server is a viable approach, but synchronization must be deliberate -- you cannot blindly export a client profile and expect it to apply cleanly to a headless server install, because some mods are client-only while others are server-only.

BepInEx: The Unity Mod Framework

A substantial portion of the popular moddable games on Steam in recent years -- Valheim, Lethal Company, Subnautica, and others -- run on the Unity engine and use BepInEx as their primary modding framework. The BepInEx GitHub repository describes it as a "plugin / modding framework for Unity Mono, IL2CPP and .NET framework games." It loads custom code (plugins) into the game at launch, patches in-game methods and classes without touching original game files, and provides configuration and logging infrastructure for plugins. The project is licensed under LGPL-2.1. Understanding which Unity backend your game uses (Mono versus IL2CPP) matters because it determines which version of BepInEx you need, as discussed below. It is also worth understanding that BepInEx provides two things: a plugin loading system (the chainloader) and a runtime patching library (Harmony, which ships bundled with BepInEx). Harmony is what allows mods to modify game behavior without replacing original files -- it patches .NET methods in memory at runtime by redirecting method calls through detour functions. When two mods both try to patch the same method, Harmony chains them so both patches run in sequence. This works well when mod authors declare dependencies properly, and produces unpredictable behavior when they do not.

$ which-framework Interactive -- answer each question to find your mod framework

What engine does your game use?

Which Unity scripting backend does your game use?

Check: if the game folder has a Managed/ directory with Assembly-CSharp.dll, it is Mono. If it has an il2cpp_data/ folder, it is IL2CPP.

What CPU architecture is your Linux server?

Run uname -m on your server. If it returns x86_64, you are on x86. If it returns aarch64, you are on ARM.

BepInEx 5 (Stable)

Use BepInEx 5.4.23.5 -- the stable release for Unity Mono games. For Valheim specifically, use the BepInExPack_Valheim from Thunderstore (not the generic GitHub release). Install the pack into the server root, make start_server_bepinex.sh executable, and launch via that wrapper script.

BepInEx 6 Bleeding Edge

IL2CPP games require BepInEx 6 bleeding edge builds (build 754+). BepInEx 5 does not support IL2CPP at all. Expect a less stable, rapidly evolving environment where game updates can break the framework itself. Download from builds.bepinex.dev.

Metamod:Source + CounterStrikeSharp

Source 2 games use an entirely different stack. Install Metamod:Source as a server plugin, then install CounterStrikeSharp (v1.0.365+, .NET 8) on top of it. Download the with-runtime package for first installs. On Linux, you will also need libicu or set DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=true.

Forge, Fabric, or Paper

Minecraft does not use BepInEx or Metamod. Server modding uses Forge (traditional modpacks), Fabric (lightweight, performance-focused), or Paper/Spigot (plugin-based). If using LinuxGSM, install the vanilla server first, then replace the server JAR with your framework of choice. As of Minecraft 26.1, Java 25 is required.

?
Check Game-Specific Documentation

Your game may use a proprietary modding system (like ARK's mod IDs), Lua-based scripting (like Don't Starve Together), or no server-side modding support at all. Check the game's official dedicated server documentation and community wiki before investing setup time. If the game runs on Unity, return to this tool and select the Unity option.

x
BepInEx Is Not Compatible with ARM

BepInEx only supports x86_64 and x86 architectures on Linux. ARM-based hosts (Oracle Cloud Ampere, AWS Graviton, Raspberry Pi) cannot run BepInEx. Attempts to use Box86/Box64 emulation have been reported as non-functional. You will need an x86_64 server for BepInEx-based modding.

On a Linux dedicated server, BepInEx behaves differently from its Windows counterpart. The startup mechanism relies on a shell script wrapper that sets environment variables for the Unity Doorstop loader, rather than a DLL injection hook into a Windows executable. The reason for this difference is that Linux has no equivalent of Windows' DLL search-order hijacking -- on Windows, BepInEx places a winhttp.dll proxy in the game directory that Unity loads automatically, giving BepInEx control before the game's own code runs. On Linux, Doorstop instead uses the LD_PRELOAD environment variable to force the dynamic linker (ld.so) to load libdoorstop_x64.so before any other shared library. The UnityDoorstop.Unix repository describes this as using "LD_PRELOAD and DYLD_INSERT_LIBRARIES injection" with plthook to intercept function calls inside the running Unity process. Once loaded, Doorstop hooks into Mono's internal assembly loading functions and redirects execution to the BepInEx preloader before the game's managed code begins. This is a more fragile mechanism than the Windows approach -- if LD_LIBRARY_PATH is not set correctly, or if the libdoorstop_x64.so binary does not match the game's architecture, the dynamic linker will silently skip the preload and BepInEx will not load at all. The BepInExPack_Valheim maintained by Azumatt, Vapok, and Margmas includes this wrapper, named start_server_bepinex.sh, which sets the necessary DOORSTOP_ENABLE, DOORSTOP_INVOKE_DLL_PATH, and LD_LIBRARY_PATH environment variables before handing off execution to the game server binary. As of early 2026, this pack is based on BepInEx 5.4.23.3 with Valheim-specific modifications and uses Doorstop 4.x (versions 5.4.2330 and newer use Doorstop 4, while older versions used Doorstop 3 with different environment variable names). This pack is specifically customized for Valheim and includes modified libraries, unstripped Unity assemblies, and preconfigured settings -- do not substitute the generic BepInEx release from GitHub, as the Valheim community pack includes patches and configuration that the upstream release does not. The unstripped assemblies are particularly important: Valheim's shipped assemblies have been "stripped" of metadata that mods need to function, and the pack bundles the full unstripped versions so that mods can access the complete Unity API.

Common Linux Error: "ld.so: object from LD_PRELOAD cannot be preloaded"

If you see the error ERROR: ld.so: object 'libdoorstop_x64.so' from LD_PRELOAD cannot be preloaded (cannot open shared object file): ignored in your server console, it means the dynamic linker cannot find the Doorstop shared library at the path specified. This usually happens for one of three reasons: the BepInEx files were extracted into the wrong directory (they must be in the server root, not in a subdirectory), the LD_LIBRARY_PATH in the startup script does not include the directory containing libdoorstop_x64.so, or the library's file permissions do not allow the steam user to read it. Verify with ls -la that the library exists alongside the game binary and is readable. If you installed BepInEx via LinuxGSM's built-in mod support, this error has been reported as a known issue where the library path is not always set correctly -- a manual install of BepInExPack_Valheim from Thunderstore resolves it.

BepInEx 5 vs. BepInEx 6: Know the Difference

As of early 2026, BepInEx 5.4.23.5 remains the stable release used by the vast majority of modded game servers running Unity Mono titles like Valheim, Lethal Company, and Subnautica. BepInEx 6 is under active development on the master branch, with a formal pre-release (6.0.0-pre.2) and ongoing bleeding edge builds available from builds.bepinex.dev. BepInEx 5 plugins are not compatible with BepInEx 6 without migration -- if you install BepInEx 6 and try to load BepInEx 5 plugins, they will silently fail to load. The BepInEx team has stated that BepInEx 5.4 is the last major version of the 5.x line and will only receive minor fixes going forward, though they intend for BepInEx 6 to eventually support loading BepInEx 5 plugins once it reaches stable release.

There is an important distinction here that many guides gloss over: BepInEx 5 only supports Unity Mono games, not Unity IL2CPP games. If the game you are modding uses IL2CPP (an increasingly common choice for newer Unity titles because it compiles C# to native code for better performance), you must use BepInEx 6 bleeding edge builds -- BepInEx 5 will not work at all. Recent bleeding edge builds (notably build #754, released February 2026) added native support for IL2CPP metadata v23-106, which covers games built with Unity 6 and later. The reason this matters is that IL2CPP games do not ship managed .NET assemblies for BepInEx to hook into, so the framework has to reverse-engineer the game's type system at startup -- a fundamentally different and more fragile process than Mono injection. For server operators running Unity Mono games, stick with BepInEx 5 unless a specific mod explicitly requires 6.x. For IL2CPP games, use the latest BepInEx 6 bleeding edge build and expect a less stable, more rapidly evolving modding environment where game updates can break the framework itself, not just individual mods.

Installing BepInEx for Valheim (Representative Example)

Valheim's modding community packages a pre-configured BepInEx build specifically for the game. The process for a Linux dedicated server is:

  1. Download the BepInExPack_Valheim archive from Thunderstore (thunderstore.io).
  2. Extract it and upload the contents -- not the outer folder itself -- directly into the Valheim server root directory via SFTP or scp.
  3. Make the startup script executable: chmod u+x start_server_bepinex.sh.
  4. Edit the exec line at the bottom of start_server_bepinex.sh to set your server name, world name, and password.
  5. Start the server using the BepInEx wrapper script, not the original valheim_server.x86_64 binary.

After first launch with BepInEx active, the framework generates the BepInEx/plugins/ directory. Individual mods -- distributed as .dll files, often inside a .zip archive -- are placed in this directory. Configuration files for each mod appear in BepInEx/config/ after the plugin runs for the first time.

Valheim's Unusual Architecture: The Server Is Not Authoritative

A detail that surprises many server administrators: Valheim's dedicated server does not simulate game logic. As the Valheim Wiki's dedicated server documentation states, "the server does not simulate any of the game logic -- it is not server-side authoritative." Instead, the nearest connected client becomes the "chunk-master" for each area and handles physics, spawning, and entity behavior locally. The server relays RPC (Remote Procedure Call) communication between clients and provides map data, but actual game simulation happens on player machines. This means that mods which change enemy behavior, loot tables, or physics run on the clients, not the server -- a server-side BepInEx plugin can only modify things like configuration synchronization, map sharing, or server-enforced rules. It also means that server CPU performance matters less than you might expect (Valheim servers are relatively light), but the client who is acting as chunk-master for a busy area becomes a bottleneck. If one player has a slow machine and is the chunk-master for a heavily built base, all other players in that area will experience lag. Additionally, Valheim's server autosaves every 20 minutes by cloning the world state in memory, which causes a brief lag spike proportional to world size -- on large, heavily built worlds, this spike can last several seconds. There is no built-in way to change this interval; it requires a BepInEx mod.

BepInEx Process Filters: Why Some Mods Skip on Servers

If you check BepInEx/LogOutput.log and see lines like [Warning: BepInEx] Skipping [ModName] because of process filters (valheim.exe), the mod is not broken -- it is intentionally configured to only load in the game client, not the dedicated server. Mod authors use the [BepInProcess] attribute to restrict which executable a plugin loads in. Client-side mods that modify the UI, camera, or local rendering set their process filter to valheim.exe (the Windows client) or valheim.x86_64 (the Linux client). The dedicated server runs as valheim_server.x86_64, so these plugins are skipped automatically. This is correct behavior and not an error. If you see dozens of "Skipping" warnings in your server log, it usually means you copied a client mod profile to the server without separating server-side plugins from client-only ones. The warnings are harmless, but they indicate mods that serve no purpose on the server and can be removed to reduce clutter.

bash -- manual BepInEx plugin installation
# After extracting a mod archive (e.g., ExampleMod.zip):
$ unzip ExampleMod.zip -d /tmp/examplemod
$ cp /tmp/examplemod/plugins/ExampleMod.dll \
  /home/steam/valheim/BepInEx/plugins/
# Restart the server for the plugin to load
# If running as a systemd service (recommended), use:
$ sudo systemctl restart valheim-modded.service
# If running manually without systemd:
$ pkill -f valheim_server    # caution: kills ALL matching processes
$ ./start_server_bepinex.sh &
ARM Architecture Incompatibility

The BepInEx installation documentation lists only x86_64 and x86 as supported architectures on Linux. If your server runs on an ARM-based host (such as Oracle Cloud Ampere instances, AWS Graviton, or Raspberry Pi), BepInEx-dependent mods will not load. Attempts to run BepInEx on ARM via Box86/Box64 emulation have been reported as non-functional on GitHub. Verify your host's CPU architecture with uname -m before building a modded stack around BepInEx.

Valheim Plus: Dormant Original, Active Forks, Native Alternatives

If you encounter guides recommending Valheim Plus as an all-in-one modding solution, the situation is more nuanced than it once was. The original Valheim Plus project has been effectively dormant since 2024. However, a community fork maintained by Grantapher on Nexus Mods continues to receive updates (v0.9.17.1 as of February 2026). Even so, the broader Valheim modding community has largely moved away from monolithic V+ in favor of standalone BepInEx plugins that are individually maintained and less likely to break wholesale on a game update. Additionally, Valheim now includes native world modifiers -- server-side settings for combat difficulty, resource rates, portal restrictions, death penalties, and more -- that can be applied via launch parameters (e.g., -modifier resources 1.5 -modifier portals free) without any mods at all. If all you want is basic difficulty and economy tuning, check native world modifiers before installing any mod framework. They work with crossplay enabled, require no client-side installation, and survive game updates without breaking.

Crossplay and BepInEx Are Mutually Exclusive (Valheim)

Valheim's crossplay feature, which enables Steam, Xbox, and PC Game Pass players to share a server, uses Microsoft's PlayFab networking backend. BepInEx hooks into the Steam networking path. When crossplay is enabled via the -crossplay launch argument, BepInEx does not load at all -- no mods will run. This is an architectural constraint, not a bug, and there is no workaround. If your group includes Xbox or Game Pass players, you must choose crossplay over mods. If everyone is on Steam, disable crossplay to use mods. This distinction is becoming more relevant in 2026 as Valheim expands to PlayStation 5 with full crossplay support -- PS5 players joining a server will require crossplay mode, which rules out BepInEx entirely. However, native world modifiers do work with crossplay enabled, so you can still customize difficulty, resource rates, and portal behavior without mods on a crossplay server. Additionally, crossplay servers tend to use roughly 1-2 GB more RAM than equivalent Steam-only servers due to PlayFab networking overhead, so factor that into your resource planning if you choose the crossplay path. A hidden dependency that trips up many Linux crossplay setups: PlayFab requires libpulse-dev and libatomic1 on the server. Without these packages, the crossplay backend fails silently. Iron Gate's official server guide documents this requirement, but many community tutorials omit it.

A Note on CS2 and Source 2 Games: Metamod, Not BepInEx

Not every moddable game uses BepInEx. Counter-Strike 2 and other Source 2 engine games use an entirely different modding stack: Metamod:Source paired with CounterStrikeSharp, a .NET 8 scripting layer that allows plugins written in C#. The CounterStrikeSharp documentation describes itself as "a simpler way to write CS2 server plugins." As of April 2026, CounterStrikeSharp is at version 1.0.365 and is actively maintained with updates tracking each CS2 game patch. If you are setting up a modded CS2 server on Linux, the BepInEx sections of this guide do not apply. The CS2 modding workflow involves installing Metamod as a server plugin, then loading CounterStrikeSharp plugins into its framework -- a fundamentally different architecture from BepInEx's Unity Doorstop-based injection. When installing CounterStrikeSharp for the first time, you must download the with-runtime package, which bundles the .NET 8 runtime. Subsequent upgrades can use the smaller package without the runtime unless a .NET version bump is required.

One important operational caveat for CS2 specifically: every time Valve pushes a CS2 update, the gameinfo.gi file that Metamod hooks into gets overwritten. This means you need to re-edit gameinfo.gi after every game update to re-add the Game csgo/addons/metamod line to the SearchPaths section, or your plugins will silently stop loading. Some hosting providers automate this step, but if you are self-managing, build it into your update workflow or script it. A simple approach is to add a post-update hook in your update script that checks whether the Metamod entry exists in gameinfo.gi and re-adds it if missing. Additionally, CounterStrikeSharp requires the .NET 8 runtime, and on Linux this means you may need to install libicu (or icu-libs on RHEL-based distributions) for .NET's globalization support. If libicu is not available or you prefer not to install it, you can set the environment variable DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=true in your server's startup script as a workaround.

Games That Cannot Be Modded on Linux (or Cannot Run at All)

Not every game has a Linux-compatible dedicated server, and not every modding framework works on Linux. Before committing to a Linux-based modded server, verify two things: that the game provides a native Linux server binary (or at least a SteamCMD-downloadable server that runs on Linux), and that the modding framework you intend to use supports Linux. Games with Windows-only dedicated servers -- such as some older Unreal Engine titles and certain indie games that only ship a Windows server executable -- cannot run on Linux without Wine or Proton, and modding under Wine adds a layer of complexity and fragility that makes it impractical for production. Similarly, as discussed earlier, BepInEx only runs on x86/x86_64 Linux -- ARM-based servers are excluded entirely. Some games technically have a Linux server but lack community modding support on that platform: mods may be compiled as Windows-only DLLs, or the modding community may only test and support Windows server installations. When evaluating whether to run a modded server on Linux, check the game's dedicated server documentation, the BepInEx or Metamod compatibility lists, and the mod pages themselves for any "Windows only" notes before investing setup time.

Migrating an Existing Windows Server to Linux

If you are moving an existing modded game server from Windows to Linux, the migration path varies by game but follows a general pattern. First, install and configure the Linux server from scratch using SteamCMD -- do not try to copy Windows server binaries to Linux, as the executables are platform-specific and will not run. Second, transfer your world save files and server configuration. For many games, save files are cross-platform: Valheim's .fwl and .db world files, Minecraft's world/ directory, and Don't Starve Together's save slots can all be copied from a Windows server to a Linux server and will load without modification. A Valheim-specific gotcha: the client stores worlds in %userprofile%/AppData/LocalLow/IronGate/Valheim/worlds_local/ (note the _local suffix), while the dedicated server uses ~/.config/unity3d/IronGate/Valheim/worlds/ (no _local). If you are importing a single-player world to a dedicated server, make sure you copy from the correct client directory. Server configuration files (server.cfg, server.properties, INI files) are also typically cross-platform, though you should review them for any Windows-specific file paths (backslashes, drive letters) that need to be converted to Linux paths. Third, reinstall your mods. BepInEx plugins (.dll files) are managed .NET assemblies and are cross-platform -- the same .dll that runs under BepInEx on Windows will work under BepInEx on Linux, provided you use the Linux version of BepInEx itself (which uses Doorstop's LD_PRELOAD mechanism rather than the Windows winhttp.dll proxy). Copy your plugins and config files from the Windows install's BepInEx/ directory to the Linux install's equivalent directory. Fourth, test thoroughly before directing players to the new server -- load the world, verify that all mods load without errors in BepInEx/LogOutput.log, and confirm that a client can connect and play normally. One detail that catches people: Valheim's startup script sets export SteamAppId=892970 (the game's client AppID, not the dedicated server AppID 896660). This is not a mistake -- the server binary needs the client's AppID to authenticate with Steam's backend services. If you write your own startup script and omit this environment variable, the server will fail to register with Steam and players will not be able to find or connect to it through the server browser.

Managing Config Files and Load Order

One of the less-discussed challenges of a modded Linux server is configuration management. Mods generate their own config files, often in game-specific locations, and keeping those synchronized between your server and your players' clients is not automatic.

For BepInEx-based games, the BepInEx/config/ directory on the server is the authoritative source. If you update a mod's configuration -- adjusting drop rates, enabling features, tuning values -- you need to restart the server for changes to take effect. The reason a restart is required is that BepInEx plugins read their configuration files once at load time and cache the values in memory; the framework does not watch config files for live changes. Some mods support config synchronization from server to client on connection (meaning the server pushes its configuration to connecting clients so everyone plays with the same rules), but this is per-mod behavior implemented by individual mod authors, not a BepInEx-wide guarantee. Always check a mod's documentation or Thunderstore page to verify whether it supports server-to-client config sync -- if it does not, you will need to distribute updated config files to players manually or through a mod manager profile export.

For ARK: Survival Ascended and similar games, mod configuration lives inside INI files -- specifically GameUserSettings.ini and Game.ini in the server's save directory. Mod IDs are added to the server startup command line as a comma-separated list. Always back up your configuration files and startup command line before applying changes -- a corrupted INI or a misplaced mod ID in the launch arguments can prevent the server from starting entirely, and rolling back without a backup means reconstructing your settings from memory.

Load order matters when you are running multiple mods that touch the same game systems. There is widespread confusion about this online, so we want to be precise: BepInEx does not load plugins in simple alphabetical order, despite what many community guides claim. The BepInEx chainloader performs a topological sort based on declared [BepInDependency] attributes -- meaning plugins are loaded in an order that satisfies their declared dependency chains. If Plugin B declares Plugin A as a dependency, Plugin A will always load first. If two plugins have no declared relationship to each other, their relative load order is not guaranteed and should not be relied upon. The reason this matters in practice is that two unrelated mods might both try to patch the same game method (using Harmony, the patching library that BepInEx ships with), and the order in which those patches are applied can produce different behavior or outright conflicts. The reason so many guides say "alphabetical" is likely because, in practice, undeclared plugins often happen to load in filesystem order -- but this is an implementation detail, not a contract. Conflicts between mods that do not declare dependencies on each other but still try to patch the same methods will surface as runtime errors in the BepInEx log, found at BepInEx/LogOutput.log.

Backing Up Before Modding

Before installing any mod stack, create a complete backup of the server directory. The reason is simple: mods modify runtime behavior in ways that can corrupt save files, and some mod removals are not cleanly reversible once a world has been loaded with them active. A pre-mod backup gives you a guaranteed rollback point. For a LinuxGSM-managed server this is a single command:

$ ./gmodserver backup

For a self-managed install, a tar archive is sufficient:

bash -- manual server backup
$ tar -cjf \
  /home/steam/backups/valheim-$(date +%Y%m%d).tar.bz2 \
  /home/steam/valheim
$ preflight-check
0/12 complete
System Setup
Mod Framework
Production Readiness

Running a Modded Server as a systemd Service

Launching a game server manually in a screen or tmux session is fine for testing, but for a server meant to survive reboots and unattended failures, a properly written systemd unit file is the right approach. systemd handles process supervision, logging to the journal, automatic restart on crash, and integration with the boot sequence.

You will find significant disagreement about this online, so we want to explain our reasoning. Many popular guides -- including LinuxGSM's own documentation -- use tmux or screen as the primary process management layer, and LinuxGSM specifically wraps game servers in tmux sessions by default. This works, and for a LinuxGSM-managed server you should use its built-in tmux approach rather than fighting against it. The reason we recommend systemd for self-managed (non-LinuxGSM) installs is that screen and tmux are terminal multiplexers, not process supervisors -- they keep a process running in the background but do not automatically restart it on crash, do not integrate with the boot sequence, and do not feed logs into the system journal. If you are already using LinuxGSM, let LinuxGSM handle process management. If you are managing the server yourself, systemd is the more robust choice for a production environment.

Here is a production-quality unit file for a BepInEx-modded Valheim server running as the steam user:

/etc/systemd/system/valheim-modded.service
[Unit]
Description=Valheim Modded Dedicated Server (BepInEx)
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=steam
Group=steam
WorkingDirectory=/home/steam/valheim
ExecStart=/home/steam/valheim/start_server_bepinex.sh
KillSignal=SIGINT
Restart=on-failure
RestartSec=30
StandardOutput=journal
StandardError=journal

# Resource limits
LimitNOFILE=100000

# Security hardening (optional but recommended)
ProtectHome=false
ProtectSystem=full
NoNewPrivileges=true
PrivateTmp=true

[Install]
WantedBy=multi-user.target

A few things worth calling out in this unit. The After=network-online.target combined with Wants=network-online.target ensures the service does not try to start before the system has full network connectivity -- important because game servers immediately try to register with external services (Steam master server, PlayFab relay, etc.) on startup and will fail or behave unpredictably without a network path. Note that After=network.target alone is not sufficient: network.target only means the networking stack is configured, not that it is reachable. network-online.target waits for an actual connection. KillSignal=SIGINT tells systemd to send SIGINT (not the default SIGTERM) when stopping the service -- Valheim's dedicated server handles SIGINT for graceful shutdown, saving world state and closing connections before exiting. This matters for games like Valheim and Minecraft where an ungraceful kill can corrupt save data. If your game server handles SIGTERM instead, change this accordingly. Restart=on-failure with RestartSec=30 means systemd will wait 30 seconds before attempting to bring the service back up after a crash, which prevents a rapid crash-restart loop from hammering your resources or hitting rate limits on external services. The LimitNOFILE setting raises the file descriptor limit because each open file, socket, and network connection consumes a descriptor; SteamCMD documentation notes that a low ulimit value is a common source of startup errors, and game servers with many connected players or loaded mods can exhaust the default 1024 limit. The security hardening directives at the bottom are optional but recommended: ProtectSystem=full makes /usr, /boot, and /etc read-only for the service, NoNewPrivileges=true prevents the process from gaining elevated privileges through setuid binaries, and PrivateTmp=true gives the service its own isolated /tmp directory. ProtectHome=false is set explicitly because the server files live under /home/steam and the service needs write access there.

After placing the unit file, reload the daemon and enable the service:

bash -- enable and start the service
$ sudo systemctl daemon-reload
$ sudo systemctl enable valheim-modded.service
$ sudo systemctl start valheim-modded.service
$ sudo systemctl status valheim-modded.service

Logs from the running service are available via journalctl, which allows filtering by time, log level, and service name -- far more flexible than tailing a flat log file:

$ journalctl -u valheim-modded.service -f --since "1 hour ago"

Firewall and Networking for Modded Servers

A modded server running publicly needs its firewall configured to expose the correct ports and nothing else. The reason this matters beyond simple connectivity is that every open port is an attack surface -- and game servers are frequent targets for DDoS attacks and exploit scanning. The ports required vary by game: Minecraft Java Edition uses TCP 25565 by default; Source engine games (CS2, Garry's Mod, TF2) use TCP and UDP 27015; Valheim uses UDP 2456-2457 (the configured port and the next one up, per Iron Gate's official documentation). Check the official dedicated server documentation for the game you are running -- do not assume defaults, because game developers occasionally change port requirements between major updates.

A quick note on the Valheim ports specifically, because this generates enormous confusion in online forums: you will find guides recommending everything from two ports (2456-2457) to three ports (2456-2458), some saying UDP only and others saying TCP/UDP, and many community posts where people argue about which port to actually connect on. Here is why: Iron Gate's official dedicated server documentation states the server uses the configured port and port+1, so 2456-2457 UDP is the authoritative answer. Port 2458 was historically used for Steam query traffic in older builds and some guides still carry it forward. Many community hosting guides and Docker images open 2456-2458 as a safety measure, which is harmless but not strictly necessary for current builds. Additionally, connecting via the Steam server browser uses port 2457 (the query port), not 2456 (the game port) -- this confuses many people who try to add a server to their favorites. The bottom line: open UDP 2456-2457 at minimum, and if you want to be safe, extend to 2458. Do not open TCP unless another service on your server specifically requires it.

$ port-lookup Select a game to see its required server ports and ufw commands

Using ufw on Ubuntu, a Garry's Mod server would be opened like this:

bash -- ufw rules for a Source engine server
# Always allow SSH first to avoid locking yourself out
$ sudo ufw allow 22/tcp
$ sudo ufw allow 27015/tcp
$ sudo ufw allow 27015/udp
$ sudo ufw allow 27020/udp  # Source TV (optional)
$ sudo ufw enable
$ sudo ufw status verbose

Mods occasionally introduce their own network behavior -- additional ports for web interfaces, RCON management, or plugin-specific communication. The CS2 modded server project on GitHub documents a known CS2 issue: RCON commands issued while directly connected to the server do not function properly. The recommended workaround is using a CounterStrikeSharp plugin called CS2Rcon that allows admins to use !rcon in chat, or disconnecting from the server and using rcon_address IP:PORT from the console externally. Review each mod's documentation for any port requirements beyond the base game.

Steam Game Server Login Tokens

For CS2, TF2, and some other Valve titles, publicly listed game servers require a Game Server Login Token (GSLT) obtained from the Steam Game Server Account Management page. A server running without a valid token will not appear in the public server browser. Each server instance requires its own unique GSLT. This is not a mod requirement specifically, but is something to set up before going live.

Security Realities of a Modded Game Server

Running a modded game server is not just a sysadmin task -- it is a cybersecurity responsibility. Game servers are high-value targets, and mods introduce attack surface that vanilla servers do not have. Understanding the threat landscape helps you make informed decisions about what you install, how you expose your server, and what monitoring you should have in place.

DDoS: Gaming Is the Top Target

Distributed denial-of-service attacks against game servers are not hypothetical -- they are routine. Help Net Security reported that gaming was the industry targeted by the highest volume of HTTP DDoS attacks in 2024, with Layer 7 incidents increasing 94 percent year over year. Richard Hummel, Senior Threat Intelligence Manager at NETSCOUT, noted that DDoS attacks "pose a serious threat to the online gambling and gaming industries." In October 2025, a suspected attack from the Aisuru botnet simultaneously disrupted Steam, Riot Games, and other major platforms, with traffic reportedly peaking above 29 Tbps. For a self-hosted game server, DDoS protection is not something you add later -- it is something you plan for from the start. At minimum, do not expose your server's real IP address in public server lists if you can avoid it, use a hosting provider with volumetric DDoS mitigation included, and configure your firewall to drop all traffic not destined for your game's specific ports.

Mod Supply Chain Attacks: The Workshop Is Not Safe by Default

Steam Workshop and community mod repositories like Thunderstore and Nexus Mods do not perform code review on uploaded content. Valve scans uploaded binaries for known malware signatures but does not analyze scripts, Lua code, or obfuscated payloads. This is not a theoretical gap -- it has been exploited repeatedly. In December 2023, the standalone Slay the Spire mod "Downfall" was hijacked via a compromised developer account and used to push password-stealing malware to players who launched the mod during a brief window. In February 2026, People Playground's developer disabled the entire Steam Workshop after a worm malware disguised as a performance mod deleted user data and propagated to other workshop items. And on April 7, 2026, 14 Project Zomboid Workshop mods were removed after developers found heavily obfuscated Lua code that created malicious files outside the game directory -- all uploaded by a single account, all disguised as add-ons to a popular soundtrack mod, and installed on between 500 and 2,200 devices before detection. The developer, The Indie Stone, stated that simply uninstalling the mods was not sufficient to remediate the threat.

The implications for server operators are direct: every mod you install on your server is third-party code running with the permissions of your server process. A malicious BepInEx plugin has full access to everything the steam user can touch -- server configuration, world saves, other mod files, and potentially credentials stored in the home directory. This is why the non-root user isolation discussed earlier in this guide is not optional. Beyond that, practical defenses include: only install mods from authors with established track records and significant download counts; check Thunderstore and Nexus comment sections for user reports before installing; never install a mod that was uploaded in the last 24-48 hours to a server you care about; and maintain backups so that a compromised mod can be rolled back without permanent data loss. If your server runs mods that accept .dll files (which BepInEx plugins are), treat every new plugin installation with the same caution you would treat running an unknown binary on a production server -- because that is exactly what it is.

RCON and Remote Management

Many game servers expose a remote console (RCON) interface for administrative commands. RCON passwords are sent in plaintext by default in Source engine games, and RCON ports are actively scanned by bots. If you expose RCON to the public internet, you will see unauthorized login attempts within hours. Best practices: bind RCON to localhost only and access it through an SSH tunnel, use a strong unique password (not the server join password), and if the game supports it, restrict RCON access by source IP in your firewall rules. For CS2 specifically, as noted earlier in this guide, in-game RCON has known issues when connected directly to the server -- using a CounterStrikeSharp plugin like CS2Rcon or an external RCON tool over SSH is more reliable and more secure.

Mod Security Is Your Responsibility

Neither Steam, Thunderstore, nor Nexus Mods guarantees that uploaded content is safe. Valve added SMS-based verification for developers pushing updates to released games in late 2023, but this only prevents unauthorized pushes to game builds -- it does not cover Workshop uploads, which any Steam user can publish. The Project Zomboid incident in April 2026 demonstrated that obfuscated malicious code can sit in the Workshop for an extended period before detection. Server operators should treat mod installation as a security decision, not just a gameplay decision.

Automated Health Monitoring for Modded Servers

A dimension of modded server administration that few guides cover in depth is proactive monitoring -- knowing your server is unhealthy before your players tell you. LinuxGSM includes built-in monitor and alert commands, but for self-managed (non-LinuxGSM) servers, you need to build your own monitoring layer. The good news is that Linux already has the tools; you just need to wire them together.

The simplest approach is a systemd watchdog combined with a cron-based health check. The watchdog catches process crashes and restarts them automatically (which the unit file in this guide already does via Restart=on-failure). But a game server process can be running without accepting player connections -- the process is alive but the server is functionally dead. This is a common failure mode with modded servers: a mod throws an unhandled exception on every tick, the server console fills with errors, and the process stays alive while being completely unresponsive to player connections.

To catch this, write a health check script that queries the server's query port (most game servers respond to A2S_INFO queries on their query port) and alerts you or restarts the service if the query fails multiple times in a row. For Source engine games, tools like qstat (also known as quakestat) can probe the query port and return server status. For Valheim and other Steam-based servers, the steam-condenser or gamedig libraries provide query functionality. A minimal watchdog script might look like this:

health-check.sh -- game server liveness probe
#!/bin/bash
set -e
# Liveness probe for a game server query port
# Run via cron every 5 minutes: */5 * * * * /home/steam/scripts/health-check.sh

QUERY_HOST="127.0.0.1"
QUERY_PORT="2457"           # Valheim query port (game port + 1)
SERVICE_NAME="valheim-modded.service"
FAIL_FILE="/tmp/.gameserver_fail_count"
MAX_FAILS="3"              # restart after 3 consecutive failures (15 min)

# Try a UDP probe (timeout 5s)
if timeout 5 bash -c "echo -n '' > /dev/udp/${QUERY_HOST}/${QUERY_PORT}" 2>/dev/null; then
  # Server responded -- reset failure counter
  rm -f "${FAIL_FILE}"
  exit 0
fi

# Server did not respond -- increment failure counter
FAILS=$(cat "${FAIL_FILE}" 2>/dev/null || echo 0)
FAILS=$((FAILS + 1))
echo "${FAILS}" > "${FAIL_FILE}"

if [ "${FAILS}" -ge "${MAX_FAILS}" ]; then
  logger -t gameserver-health "Server unresponsive after ${FAILS} checks -- restarting ${SERVICE_NAME}"
  sudo systemctl restart "${SERVICE_NAME}"
  rm -f "${FAIL_FILE}"
fi

This script probes the server's query port every 5 minutes (via cron) and only restarts after three consecutive failures, preventing false positives from momentary network blips or save-related lag spikes. The logger call writes the restart event to the system journal so you can audit why restarts happened. For more sophisticated monitoring, LinuxGSM's alert system can send notifications to Discord, Telegram, Slack, Pushbullet, email, and other channels when it detects a server issue -- if you are using LinuxGSM, enable alerts rather than writing your own scripts.

For resource-level monitoring (tracking RAM, CPU, and disk usage trends over time rather than just checking "is the process alive"), htop gives you a real-time snapshot, but for historical data you need something that records metrics over time. A lightweight approach for a single server is a cron job that appends resource snapshots to a log file every minute, which you can review when diagnosing performance degradation after adding a new mod:

$ * * * * * echo "$(date +\%Y-\%m-\%d_\%H:\%M) $(free -m | awk 'NR==2{printf "RAM: %s/%sMB (%.1f%%)", $3,$2,$3*100/$2}')" >> /home/steam/logs/resource.log

For operators running multiple servers or wanting dashboards, Prometheus with node_exporter and Grafana provides the industry-standard monitoring stack. This is likely overkill for a single game server, but if you are already running other services on the same machine, adding game server metrics to an existing Prometheus instance is trivial. The key metrics to watch for modded servers specifically are: RSS (resident set size) memory of the game server process (mods that leak memory will show a steady upward trend), disk I/O during save intervals (mods that add large amounts of world data will extend save times), and journal error rates (a sudden spike in BepInEx or game server errors after adding a mod indicates an incompatibility).

Resource Planning: RAM, CPU, and Disk

Mods consume resources. A vanilla Garry's Mod server is lightweight compared to a heavily modded one running custom gamemodes, large workshop asset packs, and multiple server-side plugins processing game logic every tick. The same principle applies across games: each mod you add increases memory pressure, may add CPU overhead on each server tick, and grows the disk footprint through asset files, logs, and save data.

A commonly cited baseline for ARK: Survival Ascended servers running a moderate mod load is 16 GB of RAM for smaller communities, scaling to 32 GB or more for large or heavily modded environments. For Minecraft with a large Forge modpack, memory allocation to the JVM is configured via the -Xms and -Xmx JVM flags. A server with 2 GB of RAM dedicated to Minecraft might start the JVM with 1 GB minimum and allow it to grow to just under 2 GB:

$ java -Xms1024M -Xmx2000M -jar server.jar nogui
Minecraft Java Version Requirements

Minecraft's Java version requirements have been increasing steadily. Minecraft 1.17 required Java 16, 1.18 required Java 17, 1.20.5 required Java 21, and as of Minecraft Java Edition 26.1 (released March 24, 2026), the game now requires Java 25 -- specifically the Microsoft Build of OpenJDK 25, which is bundled with the official launcher but not with headless server installs. Minecraft 26.1 also changed default RAM allocation from 2 GB to 4 GB and switched garbage collection from G1GC to ZGC. If you are using LinuxGSM to manage your Minecraft server, be aware that LinuxGSM installs the default JRE from your distribution's package manager, which may be an older version (Ubuntu 24.04 ships with OpenJDK 21). You will need to manually install a newer JRE and configure LinuxGSM's javaram and executable settings in mcserver.cfg to point to the correct Java binary. Always check the official Minecraft server download page for the current Java version requirement before setting up a new server.

VPS vs. Dedicated vs. Home Hosting

Where you host matters as much as how much hardware you have. A VPS (virtual private server) from a provider like Hetzner, OVH, Linode, or Vultr is the sweet spot for small to medium modded servers: you get a public IP, DDoS mitigation (basic or premium depending on provider), and the ability to scale RAM and CPU without buying hardware. For a lightly modded Valheim or Minecraft server with 5-10 players, a 4 GB RAM VPS with 2 vCPUs is usually sufficient. For heavily modded ARK or servers with 20+ concurrent players, a dedicated server with 16-32 GB RAM and NVMe storage is more appropriate -- VPS "burstable" CPU is often inadequate for game servers that need sustained single-core performance on every tick. Home hosting on a spare machine or NUC is viable for friends-only servers if you have a stable internet connection with decent upload bandwidth (at least 10 Mbps upstream) and are willing to configure port forwarding on your router. The tradeoff is that your home IP is exposed to anyone who connects, you have no upstream DDoS protection, and your server goes down when your ISP does. For anything beyond a small private group, a VPS or dedicated server is the more resilient choice.

More mods and more concurrent players both push resource requirements up. An important caveat for Minecraft JVM tuning specifically: setting -Xms and -Xmx to the same value is a common recommendation you will see online, and while it eliminates the overhead of heap resizing, it also means the JVM will never release unused memory back to the OS. On a shared host where other services also need memory, use a lower -Xms and a higher -Xmx to let the JVM grow into its allocation only when needed. For heavily modded Minecraft servers (100+ mods with Forge or Fabric), 6-8 GB of JVM heap is common, and large modpacks with worldgen or dimension mods can push this to 10 GB or higher. Monitor actual memory usage with htop or free -h under load and adjust accordingly. On disk, use SSDs or NVMe storage where possible. The reason is not just general performance: game servers with large mod packs load asset files into memory during startup and when players connect, and this I/O is random-access rather than sequential. On spinning disk, a heavily modded server can take several minutes to start and may cause visible hitching when new players join and trigger asset loads. NVMe storage reduces these load times to seconds rather than minutes. Additionally, large mod packs with significant asset content produce noticeable world-save delays on spinning disk, which can cause periodic lag spikes during auto-save intervals.

Troubleshooting Common Mod Problems

Even with careful setup, things break. Understanding where to look first saves hours of guesswork.

Server Starts but Mods Do Not Load

For BepInEx servers, the first place to check is BepInEx/LogOutput.log. BepInEx writes a startup trace that lists every plugin it attempts to load and reports any that fail with an exception. A missing dependency will show up as a FileNotFoundException for another mod's DLL. A version mismatch between a plugin compiled against an older game version and the current game binary will show up as type-load or method-not-found exceptions. The reason BepInEx uses exceptions rather than graceful fallback is that .NET assembly loading is strict about type signatures -- if a mod expects a method with a certain signature and the game's code has changed that signature in an update, the runtime cannot safely load the mod at all.

If LogOutput.log does not exist at all after starting the server, BepInEx itself is not loading. On Linux, this usually means one of three things: you are launching the game server binary directly instead of using the BepInEx startup wrapper script (start_server_bepinex.sh), the wrapper script is not marked executable (chmod u+x), or the DOORSTOP_ENABLE environment variable is not being set to TRUE in the script. Check all three before looking deeper.

Players Cannot Connect After Adding Mods

The single largest cause of connection failures after adding mods is client-server mod mismatch: the server has mods installed that require matching client-side content, but some connecting players lack those mods or have wrong versions. The reason this breaks connections (rather than simply degrading the experience) is that many mods modify network packets, entity definitions, or item registries, and when the client and server disagree on these definitions, the networking layer rejects the connection rather than risk data corruption.

The solution is a deliberate mod synchronization workflow. For Valheim and other Thunderstore-based ecosystems, the most reliable approach is to use r2modman or the Thunderstore Mod Manager. Create a profile in the mod manager with your exact server mod list, export it as a profile code or file, and share that profile with your players. When they import the profile, the mod manager downloads and installs the exact same mod versions you are running on the server -- no manual file copying, no version guesswork. Update the profile and reshare it whenever you change your server's mod stack. For Minecraft, CurseForge and Modrinth both support modpack export and import, and launchers like Prism Launcher and the CurseForge app can install server-matching modpacks in a few clicks. For games without a mod manager ecosystem, maintain a plain-text manifest listing each mod name, version, and download URL, and distribute it to players through your server's Discord or website. The key insight is that mod synchronization is a communication problem, not a technical one -- the tools exist, but they only work if you use them proactively and keep them updated.

Server Crashes Repeatedly After Update

When a game patches and a mod has not been updated for the new version, incompatibilities typically manifest as immediate crashes on startup or, in some cases, the server launching but behaving erratically (missing items, broken spawns, physics glitches). The reason crashes are the more common outcome is that game updates often change method signatures, class hierarchies, or internal data structures that mods hook into via Harmony patches -- when BepInEx tries to apply a patch to a method that no longer exists or has a different signature, the runtime throws an unrecoverable exception. The systemd journal will contain the relevant error output if you are running as a service. The safest recovery is to disable newly incompatible mods by moving them out of BepInEx/plugins/ temporarily (do not delete them -- just move them to a holding directory), restore a pre-update backup of the game binary if needed, and wait for mod authors to release updated versions. Mod repository pages on Thunderstore and Nexus Mods usually note compatibility status in update changelogs. A practical workflow for handling game updates on a modded server: pause automatic game updates, let the update land, check each mod's page for compatibility confirmation, test on a staging instance, and only then apply to production.

Checking Logs Efficiently

For systemd-managed servers, the journal aggregates output from the server process. For servers managed via LinuxGSM, script logs, console logs, and game-specific logs are written to separate files under the server's log/ directory. The LinuxGSM documentation distinguishes between its own script logs and the game server's output logs, and both should be checked when diagnosing an issue.

The Bigger Picture: Maintenance is the Job

Standing up a modded game server on Linux is genuinely accessible with the tools available in 2026. SteamCMD handles the server binary lifecycle. BepInEx handles Unity plugin injection with a shell script wrapper (while Metamod and CounterStrikeSharp handle Source 2 games like CS2). LinuxGSM handles orchestration for those who want it. systemd handles process supervision and logging. The individual pieces are documented and, taken one at a time, straightforward.

What is less often said is that modded server administration is an ongoing maintenance task, not a one-time setup. Game patches drop without warning. Mods lag behind game versions for days or weeks. Workshop content can be retracted by authors. Configuration drift accumulates. The operators who run stable, well-regarded modded servers over the long term are the ones who treat it as an operational discipline: scheduled backups, staged update testing, clear communication with players about mod requirements, and systematic log review when something behaves unexpectedly.

A few concrete practices that distinguish well-run modded servers from chaotic ones: maintain a plain-text manifest of every mod, its version, and its Thunderstore or Nexus page URL so you can quickly check compatibility when a game patches. Keep a staging instance -- even if it is just a second install directory on the same machine -- where you test updates before they hit production. Write a one-page document for your players that explains exactly how to install the required client-side mods, including which mod manager to use and how to import a profile. Automate your backups with a cron job that runs before the nightly update check, not after. And when something breaks, read the log before asking for help -- BepInEx/LogOutput.log and journalctl -u yourservice between them will tell you what went wrong in the vast majority of cases.

Linux gives you the tools to do all of that well. The rest is process.

Sources and Further Reading