Setting up PCSX2 sunshine streaming

Setting up PCSX2 sunshine streaming

After I finally beaten TLOU2 (The Last of Us 2) I thought I was ready to play GoW (God of War) saga from beginning to end. One problem arisen, though: My PS4 did not have GoW 1 or 2 at its library, not even available via backwards compatibility or PS Plus or such (region lock, I guess, I live in Brazil).

Then I decided to emulate it. But since I like to play in my switch a lot, I thought I could stream it from my headless server running Proxmox and a Debian VM. Since now we have an open source server for Moonlight, and Atmosphere (a custom rom for hacked switches) does have a moonlight client available, I decided to test this out.

Just a few notes on the host VM:

  • I used a Debian 11 guest in a proxmox setup;
  • While trying to run PCSX2 in the container, I’ve got some errors about a few instructions sets that are needed to run the emulator. This happened because I’ve set the CPU to qemu64 (or something like that) and PCSX2 wasn’t able to recognize it. I’ve had to then turn off the VM and change the CPU to “host” and the emulator launched as expected;
  • Also after getting SSH access to your VM, completely disable any screen, setting it to “none” in proxmox’s settings. This is needed to make the containers user the correct VGA;
  • You must disable secure boot (I’ve taken notes, they are later in the text), or the NVIDIA drivers won’t be loaded at the VM startup;
  • This VM uses a passed-through gtx 1060 NVIDIA gpu, so we need to make several tweaks in the code to make the containers work well with this setup.

Also, I should highlight that there are a lot of code copy and paste in certain moments of this tutorial. This means that I’ll turn it into a github project later, this is just for documentation purposes for the future me to get things done the right way.

That said, let’s keep going.

Games on Whales

One set of containers that help achieving this is Games on Whales. The project aims to solve this problem by using a combination of Docker and Sunshine server (the open source moonlight server I mentioned before). The idea here was then to use PCSX2 to emulate PS2 games.

Setting up the host

I’ve already had a Debian guest running in my proxmox. But I still needed to do some adjustments, since I have just installed it. So let’s make this fine tuning.

First of all, I’ve had to install NVidia drivers. Make sure, first, that you have aria2 and wget installed:

apt install aria2 wget -y 

Then download the driver:

#You chan change this version as needed:
export NVIDIA_VERSION=525.60.13
aria2c -x 16 -s 16 https://http.download.nvidia.com/XFree86/Linux-x86_64/${NVIDIA_VERSION}/NVIDIA-Linux-x86_64-${NVIDIA_VERSION}.run

Install kernel headers:

apt-get install linux-headers-`uname -r` build-essential -y

Run the driver installation:

chmod +x ./NVIDIA-Linux-x86_64-${NVIDIA_VERSION}.run
./NVIDIA-Linux-x86_64-${NVIDIA_VERSION}.run

If the installation complains about signing the kernel module you can refuse. The kernel module loading may require you to disable secure boot. This was my case since I’ve got the following message:

"ERROR: The kernel module failed to load. Secure boot is enabled on this system, so this is likely because it was not signed by a key that is trusted by the kernel. Please try installing the driver again, and sign the kernel module when prompted to do so."

To do this, I needed to access the VM setup in proxmox console. Get there and reboot the VM, once you see the proxmox logo press Esc. Once in the blue/gray screen, go to Device Manager > Secure Boot Configuration > Attempt Secure Boot State and press space to disable it. Press F10 and save. Then press Esc until you can exit and reset.

Once you’re the VM boots, SSH into the host and try to run the driver installer again.

Also the driver installation may complain about nouveau and it will ask if you want to block it by adding some configuration files. Accept, reboot the VM and try to run the installer again.

If the driver installation asks to add 32-bit libraries, accept.

If the driver installation asks to generate xorg configuration files, deny.

To check whether the driver is working or not after the installation process is finished, run:

nvidia-smi

If everything went accordingly, the output will be similar to this:

Tue Feb 21 19:07:04 2023       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 525.60.13    Driver Version: 525.60.13    CUDA Version: 12.0     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                               |                      |               MIG M. |
|===============================+======================+======================|
|   0  NVIDIA GeForce ...  Off  | 00000000:06:10.0 Off |                  N/A |
| 16%   41C    P0    25W / 120W |      0MiB /  6144MiB |      1%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processes:                                                                  |
|  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
|        ID   ID                                                   Usage      |
|=============================================================================|
|  No running processes found                                                 |
+-----------------------------------------------------------------------------+

Also, let’s install NVidia Container Toolkit (needed to use GPU drivers inside the container). First, install curl:

apt install curl -y

Now we add the GPG key:

curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg

Then we create the apt source list file:

cat << "EOF" > /etc/apt/sources.list.d/nvidia-container-toolkit.list
deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://nvidia.github.io/libnvidia-container/stable/debian11/$(ARCH) /
EOF

Then finally we install it:

apt update && apt install -y nvidia-docker2

Reboot so changes can take effect!!

QEMU Agent

One thing that should be mandatory is the usage of QEMU Guest Agent. This is needed due to the necessity of some operations (like VM shutdown, in my case) to be done safely.

By installing it, I was able to shutdown the VM safely when I sent a poerwoff command to the host. Since I didn’t want my gaming PC to be on all the time and make my electricity bill sky-rocket, I would eventually shut it down from the network. But this would corrupt some of the guests HDD. With QEMU Guest Agent the VMs shutdown would be done gracefully.

First of all, you should make sure that is it enabled in proxmox. In the web interface, by going to Datacenter > Host > Guest > Options, make sure that QEMU Guest Agent is enabled (if you’ve just set it, reboot the guest so changes can take effect).

Once you’re on the VM terminal again, run this to enable the agent:

systemctl enable qemu-guest-agent
systemctl start qemu-guest-agent

To test it, check if it’s working by running the following command in the host:

qm agent <vmid> ping

If no output, no error.

Before running Games on Whales, we still need to configure a virtual monitor, let’s do this.

Virtual Monitor

First of all, we need a custom edid.txt. To do this, paste all of the following lines in the terminal as root:

cat << EOF > /usr/share/X11/edid.txt
00 ff ff ff ff ff ff 00 1e 6d f5 56 71 ca 04 00 05 14 01 03 80 35 1e 78 0a ae c5 a2 57 4a 9c 25 12 50 54 21 08 00 b3 00 81 80 81 40 01 01 01 01 01 01 01 01 01 01 1a 36 80 a0 70 38 1f 40 30 20 35 00 13 2b 21 00 00 1a 02 3a 80 18 71 38 2d 40 58 2c 45 00 13 2b 21 00 00 1e 00 00 00 fd 00 38 3d 1e 53 0f 00 0a 20 20 20 20 20 20 00 00 00 fc 00 57 32 34 35 33 0a 20 20 20 20 20 20 20 01 3d 02 03 21 f1 4e 90 04 03 01 14 12 05 1f 10 13 00 00 00 00 23 09 07 07 83 01 00 00 65 03 0c 00 10 00 02 3a 80 18 71 38 2d 40 58 2c 45 00 13 2b 21 00 00 1e 01 1d 80 18 71 1c 16 20 58 2c 25 00 13 2b 21 00 00 9e 01 1d 00 72 51 d0 1e 20 6e 28 55 00 13 2b 21 00 00 1e 8c 0a d0 8a 20 e0 2d 10 10 3e 96 00 13 2b 21 00 00 18 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 26
EOF

We now add the edid.txt file to xorg-screen.conf. Again, paste all the lines below as root:

cat << EOF > /etc/X11/xorg-screen.conf
Section "Screen"
    Identifier     "Screen0"
    Device         "Device0"
    Monitor        "Monitor0"
    DefaultDepth    24
    Option         "AllowEmptyInitialConfiguration" "True"
    Option         "UseDisplayDevice" "DP-0"
    Option         "CustomEDID" "DP-0:/home/retro/edid.txt"
    Option         "ConnectedMonitor" "DP-0"
    SubSection     "Display"
        Depth       24
    EndSubSection
EndSection
EOF

Next, let’s go to the docker part.

Installing docker

Install docker using this command:

apt install docker.io -y

Install docker-compose:

wget -O /usr/bin/docker-compose https://github.com/docker/compose/releases/download/v2.15.0/docker-compose-linux-x86_64
chmod +x /usr/bin/docker-compose

Install Games on Whales

First install git:

apt install git -y

Then clone the repository:

git clone https://github.com/games-on-whales/gow.git

Enter it:

cd gow

Edit some files to match our headless setup. First edit compose/headless.yml:

vim compose/platforms/headless.yml

Turn this section:

services:
  xorg:
    image: ghcr.io/games-on-whales/xorg:edge
    # Most people will probably prefer to pull the pre-built images, but if you
    # prefer to build yourself you can uncomment these lines
#   build:
#     context: ./images/xorg
#     args:
#       BASE_IMAGE: ${BUILD_BASE_IMAGE}
#       BASE_APP_IMAGE: ${BUILD_BASE_APP_IMAGE}
    runtime: ${DOCKER_RUNTIME}
    network_mode: ${UDEVD_NETWORK}
    privileged: true
    volumes:
      # Shared with Sunshine in order to get mouse and joypad working
      - /dev/input:/dev/input:ro
      - udev:/run/udev/:ro
      # The xorg socket, it'll be populated when up and running
      - ${XORG_SOCKET}:/tmp/.X11-unix
      # run-gow: xorg_driver

I’ve added three lines in the “volumes” key. The section should look like this after the alteration:

services:
  xorg:
    image: ghcr.io/games-on-whales/xorg:edge
    # Most people will probably prefer to pull the pre-built images, but if you
    # prefer to build yourself you can uncomment these lines
#   build:
#     context: ./images/xorg
#     args:
#       BASE_IMAGE: ${BUILD_BASE_IMAGE}
#       BASE_APP_IMAGE: ${BUILD_BASE_APP_IMAGE}
    runtime: ${DOCKER_RUNTIME}
    network_mode: ${UDEVD_NETWORK}
    privileged: true
    volumes:
      #added the system_bus_socket, the edid.txt and xorg-screen.conf volumes
      - /run/dbus/system_bus_socket:/run/dbus/system_bus_socket
      - /usr/share/X11/edid.txt:/home/retro/edid.txt:ro
      - /etc/X11/xorg-screen.conf:/usr/share/X11/xorg.conf.d/01-xorg-screen.conf:ro
      # Shared with Sunshine in order to get mouse and joypad working
      - /dev/input:/dev/input:ro
      - udev:/run/udev/:ro
      # The xorg socket, it'll be populated when up and running
      - ${XORG_SOCKET}:/tmp/.X11-unix
      # run-gow: xorg_driver

Also, in the same file, there’s another section. And the system_bus_socket volume here too. This section should look like this before the change:

  # PulseAudio is used for streaming sound
  pulse:
    image: ghcr.io/games-on-whales/pulseaudio:edge
#   build:
#     context: ./images/pulseaudio
#     args:
#       BASE_IMAGE: ${BUILD_BASE_IMAGE}
#       BASE_APP_IMAGE: ${BUILD_BASE_APP_IMAGE}
    ipc: ${SHARED_IPC}
    volumes:
      - ${PULSE_SOCKET_HOST}:${PULSE_SOCKET_GUEST}

After:

  # PulseAudio is used for streaming sound
  pulse:
    image: ghcr.io/games-on-whales/pulseaudio:edge
#   build:
#     context: ./images/pulseaudio
#     args:
#       BASE_IMAGE: ${BUILD_BASE_IMAGE}
#       BASE_APP_IMAGE: ${BUILD_BASE_APP_IMAGE}
    ipc: ${SHARED_IPC}
    volumes:
      - ${PULSE_SOCKET_HOST}:${PULSE_SOCKET_GUEST}
      - /run/dbus/system_bus_socket:/run/dbus/system_bus_socket

Also we edit headless.env to match our setup:

vim env/headless.env

Change these lines to match you preferences. In my case, since I wanted to play on my switch, I changed the resolution to 720p. I’ve also changed the XORG_DISPLAY_PORT to DP-0. This is how it was before:

XORG_RESOLUTION=1920x1080
XORG_REFRESH_RATE=60
XORG_DISPLAY_PORT=HDMI-0
XORG_FORCE_RESOLUTION=false

After:

XORG_RESOLUTION=1280x720
XORG_REFRESH_RATE=60
XORG_DISPLAY_PORT=DP-0
XORG_FORCE_RESOLUTION=false

Make sure thar XORG_DISPLAY_PORT is set to DP-0!

Now we create a local_state folder:

mkdir local_state

Apply the needed permissions (for now, unfortunately, we need to 777 it):

chmod 777 local_state

Edit your user.env and set your timezone.

vim user.env

Before the editing my file was like this:

local_state=./local_state

TIME_ZONE=Europe/London

PUID=1000
PGID=1000

After:

local_state=./local_state

TIME_ZONE=America/Fortaleza

PUID=1000
PGID=1000

To test it, we use retroarch container. Pull the container image:

./run-gow --gpu nvidia --platform headless --app retroarch pull

Run it:

./run-gow --gpu nvidia --platform headless --app retroarch up

At this point you can test if everything is working as expected. Go to https://your-vm-ip:47990/ to pair your device. You can follow this guide to know how to do this. Once you finished your testing, press Ctrl + C on your terminal to finish the containers.

Once tested, bring down the containers and remove them. This is needed because we are rebuilding images ahead:

./run-gow --gpu nvidia --platform headless --app retroarch down
docker container prune

Creating a PCSX2 custom container in Games on Whales

Create a new folder called pcsx2:

mkdir images/pcsx2

Create a new folder called scripts:

mkdir images/pcsx2/scripts

Create a Dockerfile (paste all lines at once):

cat << "EOF" > images/pcsx2/Dockerfile
ARG BASE_APP_IMAGE

# hadolint ignore=DL3006
FROM ${BASE_APP_IMAGE}

ARG DEBIAN_FRONTEND=noninteractive
ARG REQUIRED_PACKAGES=" \
    libvulkan1 \
    software-properties-common \
    "

RUN apt-get update && \
    apt-get install $REQUIRED_PACKAGES -y

RUN add-apt-repository -y ppa:pcsx2-team/pcsx2-daily 

RUN apt-get update && \
    apt-get install -y pcsx2-unstable

ENV XDG_RUNTIME_DIR=/tmp/.X11-unix

COPY --chmod=777 scripts/startup.sh /opt/gow/startup-app.sh

ARG IMAGE_SOURCE
LABEL org.opencontainers.image.source $IMAGE_SOURCE
EOF

Create a startup script:

cat << "EOF" > images/pcsx2/scripts/startup.sh
#!/bin/bash
set -e

source /opt/gow/bash-lib/utils.sh

gow_log "Starting PCSX2"

CFG_DIR=$HOME/.config/PCSX2

exec /usr/bin/pcsx2-qt

EOF

Create a pcsx2.yml in the apps folder:

cat << "EOF" > compose/apps/pcsx2.yml
#########################
# pcsx2.yml
#########################
#
# This container runs PCSX2

services:
  ####################
  pcsx2:
    build:
      context: ./images/pcsx2
      args:
        BASE_IMAGE: ${BUILD_BASE_IMAGE}
        BASE_APP_IMAGE: ${BUILD_BASE_APP_IMAGE}
    runtime: ${DOCKER_RUNTIME}
    privileged: true
    network_mode: ${UDEVD_NETWORK}
    volumes:
      # Followings are needed in order to get joystick support
      - /dev/input:/dev/input:ro
      - udev:/run/udev/:ro
      # Xorg socket in order to get the screen
      - ${XORG_SOCKET}:/tmp/.X11-unix
      # Pulse socket, audio
      - ${PULSE_SOCKET_HOST}:${PULSE_SOCKET_GUEST}
      # Home directory: retroarch games, downloads, cores etc
      - ${local_state}/:/home/retro/
      # some emulators need more than 64 MB of shared memory - see https://github.com/libretro/dolphin/issues/222
      # TODO: why shm_size doesn't work ??????
      - type: tmpfs
        target: /dev/shm
        tmpfs:
          size: ${SHM_SIZE}
    ipc: ${SHARED_IPC}  # Needed for MIT-SHM, removing this should cause a performance hit see https://github.com/jessfraz/dockerfiles/issues/359
    env_file:
      - config/common.env
      - config/xorg.env
      # run-gow: gpu_env

    environment:
      # Which devices does GoW need to be able to use? The docker user will be
      # added to the groups that own these devices, to help with permissions
      # issues
      # These values are the defaults, but you can add others if needed
      GOW_REQUIRED_DEVICES: /dev/uinput /dev/input/event* /dev/dri/* /dev/snd/*
EOF

Build the images using the command below:

./run-gow --gpu nvidia --platform headless --app pcsx2 build

To test (after the long process of image building), use:

./run-gow --gpu nvidia --platform headless --app pcsx2 up

If everything went right, you should see pcsx2 window shown in your moonlight client. Still we cannot play, yet. We need the bios and the games. Let’s do this then.

After the test is done, stop the containers by pressing Ctrl + C.

Putting bios and games in the right places

Finally let’s put some bios and games. If you made the test from the section above, you’ll see that PCSX2 already created its config files under ./local_store/.config/PCSX2.

All you need to do is to put your bios under ./local_store/.config/PCSX2/bios and your games in ./local_store/.config/PCSX2/games.

Putting containers to always restart

Now the final touch is to edit a few files to make the service restart after we reboot the VM. We add restart: always in a few services in compose files. First, edit headless.yml:

vim compose/platforms/headless.yml

Here you will need to add restart: always under those three services (xorg, pulse and udevd). I’ll only post the file after adding the key in all services. Here’s the edited file:

#########################
# headless.yml
#########################
# This file contains services that are required if you want to run GoW in a
# headless environment (ie, on a host that does not have its own Xorg server)

services:
  xorg:
    restart: always
    image: ghcr.io/games-on-whales/xorg:edge
    # Most people will probably prefer to pull the pre-built images, but if you
    # prefer to build yourself you can uncomment these lines
#   build:
#     context: ./images/xorg
#     args:
#       BASE_IMAGE: ${BUILD_BASE_IMAGE}
#       BASE_APP_IMAGE: ${BUILD_BASE_APP_IMAGE}
    runtime: ${DOCKER_RUNTIME}
    network_mode: ${UDEVD_NETWORK}
    privileged: true
    volumes:
      - /usr/share/X11/edid.txt:/home/retro/edid.txt:ro
      - /etc/X11/xorg.conf:/usr/share/X11/xorg.conf.d/01-xorg-screen.conf:ro
      # Shared with Sunshine in order to get mouse and joypad working
      - /dev/input:/dev/input:ro
      - udev:/run/udev/:ro
      # The xorg socket, it'll be populated when up and running
      - ${XORG_SOCKET}:/tmp/.X11-unix
      # run-gow: xorg_driver

    ipc: ${XORG_IPC} # Needed for MIT-SHM, removing this should cause a performance hit see https://github.com/jessfraz/dockerfiles/issues/359

    env_file:
      - config/common.env
      - config/xorg.env
      # run-gow: gpu_env

    environment:
      RESOLUTION: ${XORG_RESOLUTION}
      CURRENT_OUTPUT: ${XORG_DISPLAY_PORT}
      REFRESH_RATE: ${XORG_REFRESH_RATE}
      FORCE_RESOLUTION: ${XORG_FORCE_RESOLUTION}

  # PulseAudio is used for streaming sound
  pulse:
    image: ghcr.io/games-on-whales/pulseaudio:edge
    restart: always
#   build:
#     context: ./images/pulseaudio
#     args:
#       BASE_IMAGE: ${BUILD_BASE_IMAGE}
#       BASE_APP_IMAGE: ${BUILD_BASE_APP_IMAGE}
    ipc: ${SHARED_IPC}
    volumes:
      - ${PULSE_SOCKET_HOST}:${PULSE_SOCKET_GUEST}
      - /run/dbus/system_bus_socket:/run/dbus/system_bus_socket

#####################
# We may not need udev, but some people have reported input issues without it;
# let's keep it around for now
  udevd:
    image: ghcr.io/games-on-whales/udevd:edge
    restart: always
#   build:
#     context: ./images/udevd
#     args:
#       BASE_IMAGE: ${BUILD_BASE_IMAGE}
#       BASE_APP_IMAGE: ${BUILD_BASE_APP_IMAGE}
#    # Setting network to host
#    # There must be a way to avoid this but I can't figure it out
#    # We need to be on the host network in order to get the PF_NETLINK socket
#    # You can listen to events even without that socket but Xorg and RetroArch will not pickup the devices
    network_mode: host
    privileged: true
    volumes:
      - udev:/run/udev/

####################
volumes:
  xorg: # This will hold the xorg socket file and it'll be shared between containers
  pulse: # This will hold the xorg socket

Do the same in pcsx2.yml:

vim compose/apps/pcsx2.yml

This is the file after adding restart: always in pcsx2 service:

#########################
# pcsx2.yml
#########################
#
# This container runs RetroArch

services:
  ####################
  pcsx2:
    restart: always
    build:
      context: ./images/pcsx2
      args:
        BASE_IMAGE: ${BUILD_BASE_IMAGE}
        BASE_APP_IMAGE: ${BUILD_BASE_APP_IMAGE}
    runtime: ${DOCKER_RUNTIME}
    privileged: true
    network_mode: ${UDEVD_NETWORK}
    volumes:
      # Followings are needed in order to get joystick support
      - /dev/input:/dev/input:ro
      - udev:/run/udev/:ro
      # Xorg socket in order to get the screen
      - ${XORG_SOCKET}:/tmp/.X11-unix
      # Pulse socket, audio
      - ${PULSE_SOCKET_HOST}:${PULSE_SOCKET_GUEST}
      # Home directory: retroarch games, downloads, cores etc
      - ${local_state}/:/home/retro/
      # some emulators need more than 64 MB of shared memory - see https://github.com/libretro/dolphin/issues/222
      # TODO: why shm_size doesn't work ??????
      - type: tmpfs
        target: /dev/shm
        tmpfs:
          size: ${SHM_SIZE}
    ipc: ${SHARED_IPC}  # Needed for MIT-SHM, removing this should cause a performance hit see https://github.com/jessfraz/dockerfiles/issues/359
    env_file:
      - config/common.env
      - config/xorg.env
      # run-gow: gpu_env

    environment:
      # Which devices does GoW need to be able to use? The docker user will be
      # added to the groups that own these devices, to help with permissions
      # issues
      # These values are the defaults, but you can add others if needed
      GOW_REQUIRED_DEVICES: /dev/uinput /dev/input/event* /dev/dri/* /dev/snd/*

Finally, edit sunshine.yml:

vim compose/streamers/sunshine.yml

You need to insert restart: always in the sunshine service:

version: "3"

services:
  ##########################################
  # There's a lot going on in this file; it's normal to get lost with all the
  # options and variables
  #
  # Before diving in, make sure to take a look at the documentation.  In
  # particular, it will be helpful to read the components-overview section
  # (https://games-on-whales.github.io/gow/components-overview.html)
  # in order to get a view on how the various components are tied together.
  ###################

  ####################
  # Sunshine is the heart of the streaming setup; it takes your desktop and
  # encodes it for delivery over the network
  sunshine:
    image: ghcr.io/games-on-whales/sunshine:edge
    restart: always
  # build:
  #   context: ./images/sunshine
  #   args:
  #     BASE_IMAGE: ${BUILD_BASE_IMAGE}
  #     BASE_APP_IMAGE: ${BUILD_BASE_APP_IMAGE}

    runtime: ${DOCKER_RUNTIME}
    ports:
      - 47984-47990:47984-47990/tcp
      - 48010:48010
      - 47998-48000:47998-48000/udp
    privileged: true
    volumes:
      # Xorg socket in order to get the screen
      - ${XORG_SOCKET}:/tmp/.X11-unix
      # Pulse socket, audio
      - ${PULSE_SOCKET_HOST}:${PULSE_SOCKET_GUEST}
      # Home directory: sunshine state + configs
      - ${local_state}/:/home/retro/
      # OPTIONAL: host dbus used by avahi in order to publish Sunshine for auto network discovery
      - ${DBUS}:/run/dbus:ro
    ipc: ${SHARED_IPC}  # Needed for MIT-SHM, removing this should cause a performance hit see https://github.com/jessfraz/dockerfiles/issues/359

    env_file:
      - config/common.env
      - config/xorg.env
      # run-gow: gpu_env

    environment:
      LOG_LEVEL: ${SUNSHINE_LOG_LEVEL}
      GOW_REQUIRED_DEVICES: /dev/uinput /dev/input/event* /dev/dri/*
      # Username and password for the web-ui at https://xxx.xxx.xxx.xxx:47990
      SUNSHINE_USER: ${SUNSHINE_USER}
      SUNSHINE_PASS: ${SUNSHINE_PASS}
      XDG_RUNTIME_DIR: /tmp/.X11-unix

volumes:
  udev:

To start Games on Whales in the background, run this command:

./run-gow --gpu nvidia --platform headless --app pcsx2 up -d

To stop the service, run this:

./run-gow --gpu nvidia --platform headless --app pcsx2 down -d

This should make Games on Whales restart after a reboot.

Reboot your VM and check if the service is back up on-line.

This should be it, WE’RE DONE!!!

References

https://linuxhint.com/nvidia-gpu-docker-containers-debian-11/#post-295282-_Toc125512693