SteamCMD is Valve's command-line version of the Steam client. It downloads and updates dedicated servers for games such as Valheim, Counter-Strike 2, Rust or Team Fortress 2 without a graphical interface. In this tutorial you will install SteamCMD on Ubuntu 24.04 under a dedicated user, download a Valheim dedicated server as a working example, run it as a systemd service and schedule automatic updates.

Prerequisites

To follow this guide you need:

  • A server running Ubuntu 24.04 LTS (x86_64), for example a CubePath VPS.
  • A non-root user with sudo privileges.
  • Enough resources for the game you plan to host. The Valheim example needs about 2 vCPU, 4 GB of RAM and 5 GB of free disk space.
  • UFW enabled, or another firewall you manage yourself.

SteamCMD only runs on x86_64. It does not work on ARM servers.

Step 1 - Installing the required 32-bit libraries

SteamCMD itself is a 32-bit program, so it needs the 32-bit C runtime. Enable the i386 architecture and install the dependencies:

sudo dpkg --add-architecture i386
sudo apt update
sudo apt install lib32gcc-s1 lib32stdc++6 ca-certificates curl tar

Confirm that the i386 architecture is now enabled:

dpkg --print-foreign-architectures
i386

Step 2 - Creating a dedicated steam user

Game servers should never run as root. Create a system account called steam with its own home directory, where SteamCMD and the game files will live:

sudo useradd -m -s /bin/bash steam

Open a shell as that user. The rest of the SteamCMD steps run as steam:

sudo -iu steam

Your prompt now shows steam@... and pwd returns /home/steam.

Step 3 - Downloading and bootstrapping SteamCMD

Valve distributes SteamCMD as a small tarball that updates itself on first run. Download it into ~/steamcmd and extract it:

mkdir -p ~/steamcmd
cd ~/steamcmd
curl -fsSLO https://steamcdn-a.akamaihd.net/client/installer/steamcmd_linux.tar.gz
tar -xzf steamcmd_linux.tar.gz
rm steamcmd_linux.tar.gz

Run it once with +quit so it downloads the rest of the client and exits:

~/steamcmd/steamcmd.sh +quit

The first run takes a minute. The end of the output looks similar to this:

[----] Verifying installation...
Steam Console Client (c) Valve Corporation - version 1727480123
-- type 'quit' to exit --
Loading Steam API...OK

Many dedicated servers look for the Steam client library in ~/.steam/sdk64. Create that link now to avoid the common steamclient.so: cannot open shared object file error later:

mkdir -p ~/.steam/sdk64
ln -sf ~/steamcmd/linux64/steamclient.so ~/.steam/sdk64/steamclient.so

Step 4 - Installing a dedicated server

Every game server on Steam has an app ID. You pass it to app_update together with the directory where the files should go. Some common IDs:

GameDedicated server app IDLogin
Valheim896660anonymous
Counter-Strike 2730anonymous
Rust258550anonymous
Team Fortress 2232250anonymous
Garry's Mod4020anonymous

Install the Valheim dedicated server into ~/servers/valheim. Note that +force_install_dir must come before +login:

~/steamcmd/steamcmd.sh +force_install_dir /home/steam/servers/valheim +login anonymous +app_update 896660 validate +quit

When the download finishes SteamCMD prints:

Success! App '896660' fully installed.

List the directory to confirm the server binary is there:

ls ~/servers/valheim/valheim_server.x86_64

The validate flag checks every file against Steam's manifest. Use it on the first install or when you suspect corrupted files. For routine updates, leave it out: it rereads the whole installation and is much slower.

Servers that require a Steam account

A few dedicated servers (for example DayZ or some Arma titles) cannot be downloaded anonymously and require an account that owns the game. Log in interactively once so SteamCMD can cache the credentials and ask for your Steam Guard code:

~/steamcmd/steamcmd.sh +login your_steam_username +quit

After the first successful login, later runs with +login your_steam_username reuse the cached session. Use a separate Steam account for servers rather than your personal one.

Step 5 - Using a SteamCMD script file

Long command lines become hard to read when you manage several servers. SteamCMD can read the same commands from a text file with +runscript. Create one file per server:

mkdir -p ~/scripts
nano ~/scripts/valheim.txt

Add the commands, one per line:

force_install_dir /home/steam/servers/valheim
login anonymous
app_update 896660
quit

Run it with an absolute path:

~/steamcmd/steamcmd.sh +runscript /home/steam/scripts/valheim.txt

When the server is already up to date, SteamCMD finishes in a few seconds with Success! App '896660' already up to date. Leave the steam shell before continuing:

exit

Step 6 - Running the server as a systemd service

A systemd unit starts the server at boot, restarts it if it crashes and runs a SteamCMD update before every start. Create the unit file:

sudo nano /etc/systemd/system/valheim.service

Paste the following, replacing your_server_name and your_server_password (at least 5 characters, and not contained in the server name):

[Unit]
Description=Valheim dedicated server
Wants=network-online.target
After=network-online.target

[Service]
Type=simple
User=steam
Group=steam
WorkingDirectory=/home/steam/servers/valheim
Environment=LD_LIBRARY_PATH=/home/steam/servers/valheim/linux64
Environment=SteamAppId=892970
ExecStartPre=/home/steam/steamcmd/steamcmd.sh +runscript /home/steam/scripts/valheim.txt
ExecStart=/home/steam/servers/valheim/valheim_server.x86_64 -nographics -batchmode -name "your_server_name" -port 2456 -world "Dedicated" -password "your_server_password" -public 1
KillSignal=SIGINT
TimeoutStartSec=600
TimeoutStopSec=120
Restart=on-failure
RestartSec=10

[Install]
WantedBy=multi-user.target

A few details matter here:

  • ExecStartPre updates the game before each start, so a restart is also an update.
  • SteamAppId=892970 is the Valheim game ID the server binary expects (the download ID 896660 is different).
  • KillSignal=SIGINT lets Valheim save the world cleanly on stop, and TimeoutStopSec gives it time to do so.

Valheim uses UDP ports 2456 and 2457. Open them in the firewall:

sudo ufw allow 2456:2457/udp

Load the unit, then enable and start it:

sudo systemctl daemon-reload
sudo systemctl enable --now valheim.service

Check the status:

sudo systemctl status valheim.service
● valheim.service - Valheim dedicated server
     Loaded: loaded (/etc/systemd/system/valheim.service; enabled; preset: enabled)
     Active: active (running) since Thu 2026-09-24 10:12:03 UTC; 45s ago

Follow the log until the world is loaded:

sudo journalctl -u valheim.service -f

A line containing Game server connected means the server registered with Steam and players can join it. Press Ctrl+C to stop following the log.

Step 7 - Scheduling automatic updates

Because the service updates itself on every start, an automatic update is just a scheduled restart. Create a oneshot service that restarts the game server:

sudo nano /etc/systemd/system/valheim-update.service
[Unit]
Description=Restart Valheim to apply SteamCMD updates

[Service]
Type=oneshot
ExecStart=/usr/bin/systemctl restart valheim.service

Then create a timer that triggers it every day at 05:00 server time:

sudo nano /etc/systemd/system/valheim-update.timer
[Unit]
Description=Daily Valheim update

[Timer]
OnCalendar=*-*-* 05:00:00
Persistent=true

[Install]
WantedBy=timers.target

Enable the timer:

sudo systemctl daemon-reload
sudo systemctl enable --now valheim-update.timer

Confirm it is scheduled:

systemctl list-timers valheim-update.timer
NEXT                        LEFT     LAST PASSED UNIT                 ACTIVATES
Fri 2026-09-25 05:00:00 UTC 18h left -    -      valheim-update.timer valheim-update.service

Pick a time with few players online, since the restart disconnects everyone.

Step 8 - Adding more servers

To host a second game, repeat the pattern with a new install directory, script file and unit. For example, a Counter-Strike 2 server uses this script at /home/steam/scripts/cs2.txt:

force_install_dir /home/steam/servers/cs2
login anonymous
app_update 730
quit

Give each server its own ports and its own systemd unit (cs2.service, rust.service and so on), so you can start, stop and update them independently. Keep an eye on RAM: each server reserves its memory as soon as it loads a map or world.

Downloading Workshop content

Some games load mods from the Steam Workshop. SteamCMD can fetch an item by game ID and Workshop item ID:

sudo -u steam /home/steam/steamcmd/steamcmd.sh +login anonymous +workshop_download_item 4000 your_item_id +quit

The files land in /home/steam/steamcmd/steamapps/workshop/content/<app_id>/<item_id>/. Many games only allow Workshop downloads from an account that owns the game, in which case you need a real login instead of anonymous.

Troubleshooting

steamclient.so: cannot open shared object file: the link from Step 3 is missing or broken. Recreate it as the steam user and restart the service.

ERROR! Failed to install app '...' (No subscription): the server cannot be downloaded anonymously. Log in with an account that owns the game, as shown in Step 4.

ERROR! Failed to install app '...' (Disk write failure): the install directory is not writable by steam or the disk is full. Check with df -h and ls -ld /home/steam/servers/*.

The service times out while starting: a large update can take longer than TimeoutStartSec. Increase it in the unit, run sudo systemctl daemon-reload and start the service again.

Players cannot see the server: confirm the ports are open with sudo ufw status and that the process is listening with sudo ss -ulpn | grep 2456.

Conclusion

You installed SteamCMD under a dedicated user, downloaded a dedicated server, turned it into a systemd service that updates itself on every start and scheduled a daily update restart. The same four pieces (install directory, script file, service and timer) work for any game on Steam. As next steps, set up regular backups of the world or save directory, restrict SSH access with key-based authentication, and review the game's own server configuration options for player limits and admin lists.