Skip to content

Instantly share code, notes, and snippets.

@joshuaboniface
Last active March 28, 2024 23:25
Show Gist options
  • Save joshuaboniface/50690ad188df15033c5f04b3cac31845 to your computer and use it in GitHub Desktop.
Save joshuaboniface/50690ad188df15033c5f04b3cac31845 to your computer and use it in GitHub Desktop.
Running Steam headless under Systemd

Initial setup guide:

https://steamcommunity.com/sharedfiles/filedetails/?id=680514371

Follow this first to get the basic manual session working.

I decided to take it one step further, and execute the whole thing from Systemd automatically on my server. This did require a bit of tweaking.

NOTE: In my usage, I named the user steam (not steamuser), it's in the group users, and its home directory is /var/home/steam; adjust any paths, etc. below to suit your actual details.

  1. Install your games when you have the monitor connected. Later, you would need a way to connect to the XSession on the remote server. Best to be prepared while it's easy. I luck out in that Civ6 starts a startup window before launching the game, so if I start streaming, I have access to the virtual desktop and the Steam client directly. YMMV.

Protip: Remove the bottom XFCE panel (Right-click it, Panel Properties, then remove it) or it will sometimes get in the way.

  1. After starting Steam once and installing your games (and thus anything from the Steam Workshop, i.e. mods), ensure that your mods are installed on a fat32 image. If not, especially for Civ6 (my main game), mods will not work properly. This is a general Linux problem with this game and potentially others, but it's a good idea I think to do this anyways.

First, to ensure the steam user can run mount, create the following at /etc/sudoers.d/steam:

Cmnd_Alias MOUNT = /usr/bin/mount, /usr/bin/umount
steam ALL=(root) NOPASSWD: MOUNT

Then actualy create the volume; in this example I create an 8GB image to hold the mods, but yours can be bigger or smaller as needed:

$ sudo su - steam
$ cd /var/home/steam/.steam/debian-installation/steamapps/workshop
$ /usr/sbin/mkfs.vfat -C -F 32 content.img 8388608  # 8 * 1024 * 1024
$ mv content content.orig
$ mkdir content
$ chattr +i content
$ sudo mount -o uid=steam,gid=users,fmask=113,dmask=002,iocharset=iso8859-1 /var/home/steam/.steam/debian-installation/steamapps/workshop/content.img /var/home/steam/.steam/debian-installation/steamapps/workshop/content/
$ mv content.orig/* content/
$ rmdir content.orig
  1. I use a script to actually start Steam so I can log its output. First, we adjust the ~/.config/autostart/steamheadless.desktop to run the script:

    ...
    Exec=/bin/bash /var/home/steam/runsteam.sh
    ...
    

Then, we write that script:

#!/usr/bin/env bash
echo "Launching Steam process via $0" &>> /var/log/steam/xfce.log
PASSWORD="$( cat ~/password )"
steam -login "djbon2112" "${PASSWORD}" &>/var/log/steam/steam.log
echo "Steam terminated, cleaning up..." &>> /var/log/steam/xfce.log
killall xfce4-session

Note that I store the password in ~/password to avoid it being in this script. This is just for convenience, it doesn't help with security (e.g. your Steam password is still visible in the ps aux output).

  1. Create that log directory (as a non-steam user!) and ensure it's owned by your steam user:

    $ sudo mkdir /var/log/steam
    $ sudo chown steam:users /var/log/steam
    
  2. Next, run env in the steam user su session. You will need these envvars for the next step.

  3. Edit your main script (I renamed it from startsteam.sh to startsession.sh) so it will (a) contain all the variables from env above (except for LS_COLORS because that's long and useless) prefixed with export, and (b) log the output to /var/log/steam/xfce.log. The list of envvars can probably be trimmed down, but I didn't care enough to do the trial-and-error needed to find out exactly which ones are needed to prevent startxfce4 from failing.

    #!/usr/bin/env bash
    
    exec &>>/var/log/steam/xfce.log
    
    echo "---------------------------------------------------------------"
    echo "Starting headless Steam session at $(date)"
    echo "---------------------------------------------------------------"
    
    # Exports from a login shell `env` command - needed for startxfce4 to function under systemd
    export SHELL=/bin/bash
    export HISTCONTROL=ignoreboth
    export LANGUAGE=en_CA.UTF-8
    export PWD=/var/home/steam
    export LOGNAME=steam
    export _=/usr/bin/env
    export HOME=/var/home/steam
    export LANG=en_CA.UTF-8
    export PROCPS_FROMLEN=36
    export LC_TYPE=en_CA.UTF-8
    export TERM=screen
    export USER=steam
    export SHLVL=2
    export PROCPS_USERLEN=12
    export LC_ALL=en_CA.UTF-8
    export PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games:/var/home/steam/.dotnet/tools
    export MAIL=/var/mail/steam
    
    # Mount our content fat32 volume (or there are issues with mods)
    sudo mount -o uid=steam,gid=users,fmask=113,dmask=002,iocharset=iso8859-1 /var/home/steam/.steam/debian-installation/steamapps/workshop/content.img /var/home/steam/.steam/debian-installation/steamapps/workshop/content/
    
    # Start pulseaudio
    pulseaudio --start
    
    # Start the XFCE session
    startxfce4
    
    # Clean up
    killall -u steam gpg-agent ssh-agent pulseaudio
    sudo umount /var/home/steam/.steam/debian-installation/steamapps/workshop/content/
    
  4. Finally, create your Systemd unit at /etc/systemd/system/steam-headless.service:

    [Unit]
    Description=Steam headless streamer session
    ConditionFileIsExecutable=/var/home/steam/startsession.sh
    After=multi-user.target
    
    [Service]
    Type=simple
    User=steam
    Group=users
    ExecStart=/bin/bash /var/home/steam/startsession.sh
    Restart=always
    
    [Install]
    WantedBy=multi-user.target
    

You can now enable and start the unit and have the headless session run all on its own in the background!

$ sudo systemctl enable steam-headless.service
$ sudo systemctl start steam-headless.service

...and you can see the logs in /var/log/steam/xfce.log and /var/log/steam/steam.log if you need to troubleshoot.

Happy streaming!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment