Automating Game Server Updates with systemd Timers and Cron

Keeping game servers updated is the most consistent maintenance task in self-hosted game hosting. Missing a game update means players cannot connect after the patch drops. Running updates manually at 3 AM is unsustainable. The solution is automation: systemd timers and cron jobs that handle SteamCMD updates, server restarts, and post-update verification without human intervention. This guide covers both approaches and shows which one fits each game server type.

If you are setting up automation on a new machine, compare dedicated server hardware on our homepage to ensure your server has the CPU and RAM to handle updates without disrupting active gameplay.

Why Automate Server Updates?

Game updates fall into three categories with different urgency levels:

  • Mandatory patches: Steam-wide updates that break the game protocol. Players cannot connect to an outdated server. These must be applied within hours of release.
  • Optional updates: Bug fixes, balance changes, and new features. Can be delayed 24–48 hours for community notice.
  • Server-only updates: Configuration changes, mod updates, and plugin patches. These are on your schedule.

Automation ensures mandatory patches are applied before your players notice the server is outdated. Without automation, you either wake up at odd hours or leave your community locked out until morning.

Method 1: systemd Timers (Recommended for Modern Linux)

systemd timers are the preferred approach for systems running systemd (Ubuntu 16.04+, Debian 8+, CentOS 7+). They provide better logging, dependency management, and integration with systemd services than cron.

Step 1: Create the Update Script

Create /usr/local/bin/update-game-server.sh:

#!/bin/bash
# Update script for game servers via SteamCMD
set -euo pipefail

SERVER_NAME="$1"
APP_ID="$2"
INSTALL_DIR="/opt/game-servers/$SERVER_NAME"
LOG_FILE="/var/log/game-updates/$SERVER_NAME.log"

# Stop the server before updating
systemctl stop "$SERVER_NAME" || true

# Run SteamCMD update
/usr/games/steamcmd +force_install_dir "$INSTALL_DIR" \
  +login anonymous \
  +app_update "$APP_ID" validate \
  +quit >> "$LOG_FILE" 2>&1

# Start the server after update
systemctl start "$SERVER_NAME"

# Log the update timestamp
echo "$(date): $SERVER_NAME updated (app $APP_ID)" >> "$LOG_FILE"

Step 2: Create the systemd Timer Unit

Create /etc/systemd/system/palworld-update.timer:

[Unit]
Description=Palworld server update timer
Requires=palworld-update.service

[Timer]
OnCalendar=*-*-* 04:00:00
RandomizedDelaySec=300
Persistent=true

[Install]
WantedBy=timers.target

Step 3: Create the systemd Service Unit

Create /etc/systemd/system/palworld-update.service:

[Unit]
Description=Palworld server update

[Service]
Type=oneshot
ExecStart=/usr/local/bin/update-game-server.sh palworld 2394010
User=steam
Group=steam

Step 4: Enable and Start the Timer

sudo systemctl daemon-reload
sudo systemctl enable palworld-update.timer
sudo systemctl start palworld-update.timer
sudo systemctl status palworld-update.timer

The server will now update daily at 4:00 AM with a random 5-minute delay to prevent update stampedes. The Persistent=true flag ensures the update runs immediately after boot if the machine was offline at 4:00 AM.

Method 2: Cron (Simple and Ubiquitous)

For systems without systemd, or for simpler setups, cron is the traditional choice. The same update script works with a cron entry:

# Edit the crontab for the steam user
sudo crontab -u steam -e

# Add these lines for different game servers
# Palworld update at 4:00 AM daily
0 4 * * * /usr/local/bin/update-game-server.sh palworld 2394010

# Minecraft Paper update at 5:00 AM daily (check API for latest build)
0 5 * * * /usr/local/bin/update-minecraft.sh

Cron advantages: simple, portable, and works on every Unix-like system. Disadvantages: no logging integration, no dependency management, and no way to prevent overlapping runs.

SteamCMD App IDs for Common Games

GameSteam App IDUpdate FrequencyNotes
Palworld2394010Weekly–monthlyLarge download (15+ GB)
Valheim896660Monthly–quarterlySmall patches
7 Days to Die294420MonthlyAlpha/beta branches available
Rust258550Monthly (forced wipe)First Thursday of month
ARK: SA2430930Weekly–monthlyVery large (40+ GB per map)
Enshrouded2278520MonthlyModerate download
Project Zomboid380870Monthly–quarterlySmall patches
Counter-Strike 2730WeeklyUse srcds_run instead
Sons of the Forest2465200MonthlyModerate download

Graceful Restart: Don’t Kick Players Mid-Game

A hard restart during an update kicks players with no warning. For a better experience, use a graceful shutdown that notifies players before stopping the server:

#!/bin/bash
# Graceful restart script for Palworld
SERVER_NAME="palworld"
RCON_PORT=25575
RCON_PASS="your-rcon-password"

# Send warning via RCON
echo "Broadcast: Server restarting in 5 minutes for update" | rcon -a 127.0.0.1:$RCON_PORT -p $RCON_PASS
sleep 240

echo "Broadcast: Server restarting in 60 seconds. Save your progress!" | rcon -a 127.0.0.1:$RCON_PORT -p $RCON_PASS
sleep 60

echo "Broadcast: Server restarting NOW" | rcon -a 127.0.0.1:$RCON_PORT -p $RCON_PASS

# Save world before shutdown
systemctl stop "$SERVER_NAME"

# Update
/usr/local/bin/update-game-server.sh "$SERVER_NAME" 2394010

# Start is handled by the update script or watchdog

Update Verification: Did It Work?

After each update, verify that the server is running and responding:

#!/bin/bash
# Post-update health check
SERVER_NAME="$1"

# Check if process is running
if pgrep -f "$SERVER_NAME" > /dev/null; then
  echo "$(date): $SERVER_NAME is running after update"
else
  echo "$(date): ERROR - $SERVER_NAME failed to start after update"
  systemctl restart "$SERVER_NAME"
  # Send alert via email or webhook
  curl -s -X POST "https://hooks.example.com/alert" \
    -H "Content-Type: application/json" \
    -d "{\"text\":\"$SERVER_NAME failed to start after update\"}"
fi

Multi-Game Server Update Orchestration

If you run multiple game servers on one machine, stagger the update times to avoid saturating the network link and disk I/O. A staggered schedule:

TimeServerApp ID
4:00 AMPalworld2394010
4:30 AMValheim896660
5:00 AMMinecraft (Paper)N/A (curl check)
5:15 AM7 Days to Die294420

Use RandomizedDelaySec=300 in systemd timers or a random sleep in cron to further spread the load.

Testing the Automation

Before trusting the automation in production, test it:

  • Run the update script manually: sudo /usr/local/bin/update-game-server.sh palworld 2394010
  • Check the log: cat /var/log/game-updates/palworld.log
  • Trigger the timer manually: sudo systemctl start palworld-update.service
  • Verify the server starts and players can connect
  • Simulate a failed update: remove the SteamCMD binary and confirm the alert fires

Summary

Automating game server updates with systemd timers or cron eliminates the most common maintenance burden of self-hosted game servers. systemd timers are the recommended approach for modern Linux distributions, offering better logging and integration with systemd services. Cron is a simpler alternative that works everywhere. Pair the automation with graceful shutdown notices and post-update verification to ensure updates happen without disrupting your players.

Review our dedicated server hosting options to find a machine with enough storage and bandwidth to handle unattended game updates.

Leave a Reply