How to Install HLDS as a Non-Root User (Best Practice)

June 4, 2026 Daemon666 8 min read 10 преглеждания

Running a game server as root is a habit worth breaking permanently. HLDS parses untrusted network traffic from anyone on the internet; if a flaw is ever exploited, the attacker inherits whatever privileges the process holds. Run it as root and that is the whole machine. Run it as a locked-down user and the blast radius is one game server's files. There is no performance cost and no functional downside, so this is simply how it should be done.

1. Why root is unnecessary here

The one reason a service traditionally needs root is binding a port below 1024. HLDS uses UDP 27015, which is well above that range, so an unprivileged user can bind it without any special capability. There is nothing HLDS does that requires elevated privileges — the only reason people run it as root is that they installed as root and never switched.

2. Create a dedicated user

Make a user that exists only to run the server, with no login password and no shell privileges beyond what it needs:

sudo adduser --disabled-password --gecos "" csserver

Use a service-specific account, not your own login account — you want the server isolated from your personal files and SSH keys. Everything the server touches will be owned by csserver.

3. Install everything as that user

Switch to the new user and install SteamCMD and HLDS from there, so every file is created with the right ownership from the start:

sudo su - csserver
mkdir -p ~/steamcmd ~/hlds
cd ~/steamcmd
curl -sqL "https://steamcdn-a.akamaihd.net/client/installer/steamcmd_linux.tar.gz" | tar zxf -
./steamcmd.sh +force_install_dir ~/hlds +login anonymous +app_update 90 validate +quit

Installing as csserver avoids the most common mistake: installing as root and later getting Permission denied when the game process cannot write its own logs or download files. The full install is in the SteamCMD guide; the 32-bit libraries it needs are in 32-bit libraries for CS 1.6.

4. If you already installed as root, fix ownership

Inherited a root-owned install? Do not reinstall — just hand it to the new user:

sudo chown -R csserver:csserver /home/csserver/hlds

Then always start the server as csserver, never with sudo. A single root-owned file in the tree (often a log or .dem written during a root test) can cause a Permission denied later, so re-run the chown if anything misbehaves.

5. Run it under systemd with dropped privileges

The clean way to keep it non-root permanently is a systemd unit that sets User=, so the service can never run as root even if you start it as root. Create /etc/systemd/system/hlds.service:

[Unit]
Description=CS 1.6 HLDS Server
After=network.target

[Service]
Type=simple
User=csserver
Group=csserver
WorkingDirectory=/home/csserver/hlds
ExecStart=/home/csserver/hlds/hlds_run -game cstrike +map de_dust2 +maxplayers 24 -port 27015
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

Enable and start it:

sudo systemctl daemon-reload
sudo systemctl enable --now hlds

The User=csserver line is the guarantee: systemd drops to that account before executing HLDS. A fuller unit with auto-restart tuning is in the HLDS systemd service guide.

6. Optional hardening

Because the service is already unprivileged, you can add cheap systemd sandboxing on top — NoNewPrivileges=true, ProtectSystem=strict with a ReadWritePaths= for the game dir, and PrivateTmp=true. These cost nothing and further shrink what a compromised process could reach. Start with just User= though; that alone is the difference that matters.

Troubleshooting

  • Permission denied writing logs or downloads — part of the tree is root-owned. Run the chown -R csserver:csserver from step 4.
  • Service fails with hlds_run: No such file or directory — the 32-bit loader is missing, not a permission issue. See the fix.
  • Works when you run it manually as root but not under systemd — that is the point; fix ownership so it runs as csserver rather than reverting to root.
  • Port bind fails — not a privilege problem on 27015; another process holds the port. Check with ss -ulpn | grep 27015.

Verification

With the service running, confirm the process is not root:

ps -o user= -C hlds_linux
systemctl status hlds

The owner should read csserver, not root. Connect to the server to confirm it plays normally as an unprivileged process — it will, because nothing HLDS does needs root. If ps shows csserver and players can join, you are running the correct, secure way. Pair this with a well-chosen VPS and a locked-down firewall.

Сътрудници: Daemon666 ✦
Сподели: