date: 2025-09-01
banner: "![[I made the jankiest cloud gaming setup possible.. and how you should do it better - banner.png]]"
banner_y: 0.07
summary: Nowadays everybody talks about cloud gaming, so i made my own cloud gaming, and you can do so too! just don't make the same mistakes that i did or it's gonna turn ugly...
read_time: ~9 minutes
authors:
- ZeroKun265
tags:
- Tech
- Linux
- Games
- Gaming
- Cloud
- Hardware
I made the jankiest cloud gaming setup possible.. and how you should do it better
~9 minutes
01/09/2025
Nowadays everyone talks about gaming on the cloud.. but as we tech literates like to say: "There is no Cloud, it's just somebody else's computer", and while Steam may have an option to hide your.. your know what games, your cloud providers don't need to add you to their friends list to see that.
And so a thought popped in my mind: "Why not just make my own cloud gaming setup? I have the skills".. little did i know: i did not, in fact, have the skills necessary to do so (kinda)... in fact, i didn't even have the hardware, although we'll get there in a second.
I was pretty sure that many had done this already, so i did some research and found out about Sunshine, "a self-hosted game stream host for Moonlight".
The idea is very simple, Sunshine is the streaming server, the "host" and Moonshine is the application used to actually play, or the "client".
The setup of these two is pretty simple, and i was able to quickly get a gaming session on my laptop from my couch streaming from my desktop upstaris.. "Great!".. but what about accessing it from outside my local network? That's where Tailscale came in, a VPN that allows you to link multiple devices by giving you an IP that you can use when and only when connected to the same Tailscale network (and because i was curious, they use a special slice of the IP adress list so as to not override any IP you might actually care about.. thought i'd mention it).. this allowed me to use my laptop with my mobile data connection and still game without issue.
That's where most people would probably stop, but if you want to go a little further you can also:
Now, no matter what happens to you (need a reboot for update? Power cuts? Anything!™), you will be able to game on your own machine, knowing nobody can spy on you, take away your service or ransom you to keep it.
That's how most people would setup their ultimate Cloud™ Gaming setup, but why did i mention "the jankiest setup possible"?
Well, keep in mind that i was about to leave the same day i was setting this up, and therefore couldn't wait for products to arrive, so i had to improvise a bit; let's go through the list of the correct setup and see what i was did correctly:
Now let's see what i did wrong/couldn't do:
Number 3, out of all, caused probably the most issues, which is why i'll cover it first:
I tried many times to make the adapter work after an update: it worked in windows, it worked before the update, it worked on my Arch Linux machine on an older kernel, it worked on the same laptop on the newest kernel.. (WHY CACHY..WHY??).. i didn't have time to buy another one (especially because i wanted to research deeply so i could buy one that had NO DRIVERS ISSUES this time..).
I spent so much trying to fix it; until i sent it back to amazon (luckily it was new enough to send it back within 30 days).. so now what? I had used the desktop for a while in USB Tethering from my second phone connected to my WiFi (hurrah for NAT and latency i guess..) so i though "AH! I just leave this plugged in, easy".
It was not, in fact, easy:
In the end i figured something out: instead of using an ADB shell on the device itself i could just send ADB commands from the desktop itself.. but without internet i couldn't send any kind of commands by hand, hence i had to automate it:
#!/bin/bash
PING_TARGET="8.8.8.8"
while true; do
if ping -c 1 -W 2 $PING_TARGET >/dev/null 2>&1; then
echo "$(date '+%F %T') - Internet is up."
else
echo "$(date '+%F %T') - No internet. Enabling USB tethering..."
adb shell "svc usb setFunctions rndis" >/dev/null 2>&1
echo "Waiting 10 seconds before next attempt..."
fi
sleep 10
done
It's ugly, and i like it exactly the way it is.. for those that can't read bash: the script just runs every 10 seconds and pings google's DNS, if successful, the script moves on and waits another 10 seconds to repeat the process, if the ping isn't successful, it sends an adb
command to enable the USB tethering (rndis
).
adb:
Android Debug Bridge, a set of tools to interface with the Android Operating System for development/debug purposes.And now to fix the simple issue of my parents touching the phone.. i just put it inside the case.. no i'm not joking, don't believe me? Look at this:
HEY, STOP LOOKING AT THE DUST AND CABLE MANAGEMENT!!.. jeez, i swear i'll clean it up once i get an actual case instead of a 20 year old hand me down..
Anyways.. this pretty much solved the networking issue (maybe not the cable management or thermal issues.. but one thing at a time alright?).
So now the second issue, the dreaded TV... i actually fixed that pretty quickly, although it kinda goes against the point of this whole exercise (i refuse to call this an actual deployment).. i just used an Amazon smart plug!
The TV had a setting to restore it's previous state after a power outage (weird, i know, for a TV? never seen that) and so i'd just cut the power whenever i was not using it (not great for the poor TV's electronics, but it is what it is).
Funnily enough, since i use KDE Plasma on Wayland, the plasmashell would sometimes crash when i turned the TV off remotely, and without plasmashell the streaming couldn't work as well; It is also impossible to restart the plasma shell through a terminal only (like ssh is)
In the end i made another script that runs on login and just waits for a file to change, and when it does, it hard resets the shell, here is the code for those interested:
#!/bin/bash
# File to watch
WATCH_FILE="$HOME/.restart_plasma.flag"
# Make sure it exists
touch "$WATCH_FILE"
# Kill and restart plasmashell when file changes
restart_plasma() {
echo "Restarting plasmashell..."
kquitapp6 plasmashell 2>/dev/null || killall plasmashell
kstart5 plasmashell &
}
# Export DISPLAY so plasmashell starts in the right session
export DISPLAY=:0
export XDG_RUNTIME_DIR="/run/user/$UID"
# Watch file indefinitely
inotifywait -m -e close_write "$WATCH_FILE" | while read -r; do
restart_plasma
done
While doing this i actually discovered that i couldn't stream my desktop UNLESS a monitor was actually plugged in and working.. so that's cool, definitely not gonna come back and bite me later.
Anyways, it came and bit me back later, when i decided to ditch the Amazon Plug, here is the issue:
This stumped me for quite a bit, and not even our AI overlords could figure that out (and i won't pay the premium tiers to try if they would've solved it), but i wouldn't be writing this article if i hadn't fixed the issue right?
First of all, how i made the dummy display: i didn't have a dummy plug and, as mentioned above, couldn't buy one in time, therefore i simulated a display using an EDID file and a kernel argument:
get-edid
, using parse-edid
you can read the capabilities of the monitor in a human readable format (the edid file used next will need to remain in binary form)virtual
) into /usr/lib/firmware/edid
(if the folder does not exist, make it)drm.edid_firmware=DP-3:edid/virtual
(Of course, replace virtual with the name of your file, it can be anything) and video=DP-3:e
DP-3
with any free video output on your graphics card, you can list your video ports and their status with this command ls /sys/class/drm/ | grep -E 'HDMI|DP|eDP|VGA'
(just remove the cardX-
prefix)FILES
in the /etc/mkinitcpio.conf
like so: FILES=(/usr/lib/firmware/edid/virtual)
mkinitcpio -p <your kernel>
, in my case mkinitcpio -p linux-cachyos
You should now have a dummy display in your DE's settings!
This is still useless tho, as without a real monitor, the kernel won't even talk to the GPU about drawing anything.. so you have to trick the kernel by forcing the main display, and you do this by using, once again, the video
kernel parameter like so: video=HDMI-A-1:D
.
HDMI-A-1
with the port used by your main monitor.This means that now at least one display is always working even though the TV isn't on, and therefore we can finally stream!!
While writing this article i just realized one could have probably just avoided the whole virtual desktop and just forced the main monitor to be always on.. i'm not gonna test it but you're welcome to do so!
Also, cut me some slack, i was tired, frustrated and time constrained..
Just because you can do something doesn't mean you should.. while experimenting i ended up trying (and succeeding) to ssh directly on the phone (wasn't helpful though), trying to embed an X11 session in my Wayland one and stream that, and many other things that didn't quite work out.
In the end, i probably should have just ditched the cloud gaming for now and come back to it with the necessary hardware.. i got better things to do anyways (University exams anyone?).. still, it was a fun process, i learned a bit more about my beloved kernel, and i learned a bit more about drm, adb and more.. and overall i'm quite happy.
Also, the multiple layers of NAT and the phone essentially being in a Faraday Cage and having a bit of poor reception make this setup pretty susceptible to network issues, and also gives the whole system a very high input latency.
Once i get out of the bus i am currently writing this article on, i will test it out, only to probably not use it..
For now this was all, thanks for reading this very dumb article, hopefully i write one before 2 years pass again..
Authors:
ZeroKun265