How to run X + Sunshine + Steam on headless server


I’m trying to run Sunshine on a headless NixOS Server + NVidia GPU + Steam (NixOS 23.05 on AWS EC2 g4dn.xlarge instance). I do not have any physical device or screen, only a Cloud server with GPU on which I simulate a screen.

See full configuration.nix here.

I got Nvidia drivers, GPU, X server, Sunshine and Steam working independently, but cannot make them play along properly.

What works:

  • I simulated a screen with NVidia GPU and X server, xrandr and nvidia-smi behave as expected
  • Using root SSH session I can start X server with startx and Sunshine server, roughly:
    ssh root@....
    $ export DISPLAY=:0
    $ startx > startx.log 2>1 &
    $ sunshine > sunshine.log 2>1 &
    But running Steam via Moonshine won’t work as Steam cannot run as root.

I guess my problem lies with how I run X server and Sunshine/Steam: I suppose Sunshine and Steam must start as non-root user, but I cannot get a proper X setup as non-root user.

I tried:

  • Auto-login as sunshine user with services.xserver.displayManager.autoLogin and configuring a Sunshine systemd user service as shown on Sunshine self-hosted game stream - #18 by NixUser80 - looks like user is logged-in but then immediately logged-out. Couldn’t move further.
  • Hacked /dev/uinput and chown sunshine /dev/tty* in order to roughly run startx and sunshine via SSH. Moonlight connection to Sunshine kinda works (I’m dropped into a shell whatever connection mode I use), in which I can run steam steam://open/bigpicture but it doesn’t have network access: I’m stuck at “Select network” in Steam startup as none is found. Stuck there for now. (in short I tried to follow Remote SSH Headless Setup - Sunshine documentation)

TL; DR, I’m looking for ways to:

  • Properly run X and subprocesses on headless server as non-root user (no physical screen, only SSH access)
  • Let Steam Big Picture see and access networks

Or maybe I took a wrong turn somewhere and should change my approach?

Thanks by advance :slight_smile:

Let Steam Big Picture see and access networks

Turns our I was missing networking.networkmanager.enable = true; - now Steam can run :partying_face: Still very hacky though.

Look into the xrdp service

wow, does this work then, and is reproducible ?

I did see attempt to sunshine to run on nix, i had a hack with it , but i didn’t have any success.

Sunshine might be really good for remote desktop, tigervnc is okay and i’ve always had success with it , but broadcasting the whole frame buffer with a video codec in hardware pushing the bits, seems to be the way to go for accessing remote resources :-).

Impressed. very impressed.

Thanks for your feedback :slight_smile: It kinda works, I was able to run Steam and a few games - but it’s still unstable and performance can be better. I’m working out how to configure codecs and PulseAudio properly.

TigerVNC and xvnc seems like good alternative, I’ll try them on as well. I’ll post updates as I move on

1 Like

I finally got it working and was able to run a few games in Ultra with quite a good experience ! :grinning:

Autologin issue was only with lightdm. Switching to gdm made it work. I’m not sure what caused the issue with lightdm but journactl errors seemed to indicate that desktop session service started before X session was ready, causing it to fail and logout.

Using gdm display manager with gnome desktop manager works like a charm now:

services.xserver = {
    enable = true;
    videoDrivers = ["nvidia"];
    displayManager.gdm.enable = true;
    displayManager.defaultSession = "gnome";

    displayManager.autoLogin.enable = true;
    displayManager.autoLogin.user = "sunshine"; # user must exists

    desktopManager.gnome.enable = true;

Configuring a user systemd service allow server to start

security.wrappers.sunshine = {
    owner = "root";
    group = "root";
    capabilities = "cap_sys_admin+p";
    source = "${pkgs.sunshine}/bin/sunshine";

# Inspired from = {
    description = "Sunshine server";
    wantedBy = [ "" ];
    startLimitIntervalSec = 500;
    startLimitBurst = 5;
    partOf = [ "" ];
    wants = [ "" ];
    after = [ "" ];

    serviceConfig = {
        ExecStart = "${}/sunshine ${configFile}/config/sunshine.conf";
        Restart = "on-failure";
        RestartSec = "5s";

Fully working config as of now:

Only client side, the moonlight-qt package is missing h264/265 codecs. Moonlight kept complaining despite installing x265, x264 and other packages that supposedly provide codecs, I’ll create an issue. However using Flatpak com.moonlight_stream.Moonlight works on a NixOS client - a bit dirty but works well for now.