···11+---
22+title: Steam Remote Play Headless
33+date: 2026-01-17
44+summary: Using gamescope headless to run Steam remote play
55+cowsay: We have Stadia at home
66+---
77+88+For a while, I've wanted a setup where I can simply SSH into my desktop, start Steam, and use remote play to play my games.
99+1010+Gamescope offers a backend called `headless` that allows me to do this, after some messing around I got it working mostly.
1111+1212+There's other software like [Sunshine](https://github.com/LizardByte/Sunshine) that seem cool and have NixOS modules. I chose to avoid these because of the extra setup they need, and my use-case is a lot simpler than what they're made for.
1313+1414+## Steam/Gamescope Setup
1515+1616+First some manual setup for Steam, I had to enable Remote Play and pair my laptop to my desktop for the first time. when you first try to connect to a device to remote play from it, you need to enter a PIN on the host.
1717+1818+Luckily I have a monitor plugged into my desktop so I just turned that on, opened Steam in a graphical session, and got everything set up. Hopefully I only have to do this once (but also Steam loves to clear my preferences so we'll see).
1919+2020+My NixOS setup for steam and gamescope is this:
2121+2222+```nix
2323+{
2424+ programs.steam = {
2525+ enable = true;
2626+ remotePlay.openFirewall = true;
2727+ extest.enable = false; # I'll explain this in a sec.
2828+ };
2929+3030+ programs.gamescope = {
3131+ enable = true;
3232+ capSysNice = false; # I'll explain this too.
3333+ };
3434+}
3535+```
3636+3737+### Extest
3838+3939+We disable extest since it's not really needed, and it will panic when you try to send any input to a game Steam launches. `false` is the default so you can omit the line, but figured I should mention it.
4040+4141+### capSysNice
4242+4343+This is an [already discussed](https://github.com/NixOS/nixpkgs/issues/351516) issue. The way I'm launching gamescope is causing issues with bubblewrap passing the sysnice capability to Steam. I just disable that and renice it later.
4444+4545+I'm like 80% sure this is a skill issue on my part to be honest. Most stuff says this has been fixed, at least in the NixOS module for Steam.
4646+4747+## Running Steam
4848+4949+With Steam set up and those options enabled, we can run gamescope. My laptop uses a 2256x1504 resolution so I'm setting that here. I set both gamescope's resolution (`-W/-H`) and Steams (`-w`/`-h`).
5050+5151+```sh
5252+gamescope -W 2256 -H 1504 -w 2256 -h 1504 --backend headless --steam -- steam -tenfoot -pipewire-dmabuf
5353+```
5454+5555+We launch gamescope with our preferred resolution, with a headless backend and Steam integration enabled. Then we launch Steam in big picture mode.
5656+5757+I'll usually start this in a GNU screen session so it doesn't stop Steam if my SSH session drops.
5858+5959+```sh
6060+screen -S steam # Before running gamescope
6161+```
6262+6363+## Renicing
6464+6565+Finally just renice all gamescope processes because it can start to degrade in performance after a while allegedly.
6666+6767+```sh
6868+sudo renice -n -20 -p $(pgrep -d " " "gamescope")
6969+```
7070+7171+Not the best solution but works fine. You could also repeat this for Steam and the game you're running once it launches.
7272+7373+## Final Results
7474+7575+On a wired connection all the games I've tested are responsive and work very well besides some minor issues that are more a Linux/NixOS thing™ than specific to this. Here's a screenshot from Portal 2 with the overlay enabled.
7676+7777+
7878+7979+Consistently I get <1ms input latency and ~20ms display latency in-game, which for me is perfectly acceptable. I'm able to have all the settings on high now without my laptop becoming a nice and toasty 100°C.
8080+8181+The nice thing is how I can choose to start this whenever I want remotely; I don't need a Steam session always open on a display on my desktop. I do wish Steam would let me add non-steam games in the big-picture UI because I still have to do that manually.
8282+8383+## Conclusion
8484+8585+I'm sure Sunshine is way better in both performance and versatility but this works well enough for me and requires far less setup that I can deal with it. Security-wise I didn't want to introduce an entirely new service just to stream games, especially since it has an entire auth system to set up.
8686+8787+Connecting via SSH is already easy since I use keys, and Steam handles everything for the actual remote play authentication for me already, so I see it as a much simpler setup.
8888+8989+Of course Sunshine does a lot more than Steam so it's not really a fair comparison. I might end up trying it anyways to see how well it works.
9090+9191+First blog post in 2 years, maybe I'll make another one this decade!
···3939 pursue a career in this industry.
4040 </p>
4141 <p>
4242- After programming on my own for about 3 years, I went to trade school for Information
4343- Technology Programming and learned about best practices and the software development life
4444- cycle. I participated in both SkillsUSA and TSA and was awarded 1st place in states for Video
4545- Game Design in TSA. I also got 1st place in Computer Programming at states and 4th at
4646- nationals in SkillsUSA. Throughout my time at trade school, I received my OSHA, IC3, and
4747- Pennsylvania State Skills (Information Technology Programming) certifications.
4848- </p>
4949- <p>
5050- Also during my time at trade school, I joined an online community of modders for the game
5151- Outer Wilds. This community helped me go above and beyond with my development, continually
5252- motivating me to try new things and publish my work.
5353- </p>
5454- <p>
5555- I'm currently in college majoring in Computer Science. I'm hoping to graduate in 2026 with a
5656- Bachelors and a minor in Professional Technical Writing. I am currently doing research on ASL
5757- translation with the Computer Science department. I'm also developing a web interface for us
5858- to use for programming competitions.
4242+ I'm currently in college majoring in Computer Science. I'm hoping to graduate in 2027 with a
4343+ Bachelors in Computer Science and a minor in Professional Technical Writing. to use for
4444+ programming competitions.
5945 </p>
6046 </div>
6147
+6-6
src/pages/resume.pdf.ts
···11-import rawTypst from "@assets/resume.typ?raw";
11+import rawTypst from "@assets/resume.typ?raw";
22import { spawn } from "node:child_process";
3344const compileTypst = (raw: string): Promise<Buffer> => {
55- const cmd = spawn("typst", ["compile", "-f", "pdf", "-", "-"], {stdio: "pipe"});
55+ const cmd = spawn("typst", ["compile", "-f", "pdf", "-", "-"], { stdio: "pipe" });
6677 cmd.stdin.write(raw);
88 cmd.stdin.end();
991010 return new Promise((resolve, reject) => {
1111 cmd.on("error", reject);
1212-1212+1313 let stderr = "";
1414 cmd.stderr.setEncoding("utf8");
1515 cmd.stderr.on("data", (data) => {
···3131 resolve(stdout as Buffer);
3232 } else {
3333 reject(new Error(`No stdout, Stderr: ${stderr}`));
3434- }
3434+ }
3535 } else {
3636 reject(new Error(`Exited with code ${code} Stderr: ${stderr}`));
3737- }
3737+ }
3838 });
3939 });
4040};
41414242export async function GET() {
4343 const data = await compileTypst(rawTypst);
4444- // @ts-expect-error "idk why it thinks this is wrong? Astro supports responses from a Buffer"
4444+ // @ts-expect-error "idk why it thinks this is wrong? Astro supports responses from a Buffer"
4545 return new Response(data);
4646}