Plan: Headless Game Streaming Server
Plan: Headless Game Streaming Server
Vision
A fully headless game streaming server — no monitor, no dummy plug, just a GPU in a k3s cluster serving Palworld to any Moonlight client on the tailnet.
Projects & Repos Touched
| Project/Repo | Platform | Role in this plan |
|---|---|---|
| forgejo_admin/palworld-server | Forgejo | All IaC changes (xorg config, deployment spec) |
Context
The palworld-server repo was built while a physical LG UltraFine monitor was connected to the GTX 1070. The game client streaming pod (Phase 2) used Sunshine/Moonlight for remote play but assumed Xorg could find a real display. Now the server is headless — no monitor, remote-only access. The NVIDIA proprietary Xorg DDX refuses to create display outputs without a real display handshake, producing NULL metamodes. Sunshine then fails to capture or encode.
What's already done:
- [x] Switched xorg config from nvidia DDX to modesetting driver
- [x] Modesetting finds DP-2 (cable still connected, monitor off) and sets 1920x1080
- [x] Sunshine starts with libx264 software encoding, KMS capture works
- [x] Dedicated server scaled back to 1 replica and running
- [ ] End-to-end Moonlight connection not tested
- [ ] No NVENC hardware encoding (modesetting doesn't expose it to Sunshine)
- [ ] Depends on DP-2 cable being physically present
Previous Plan
None — first plan for this project.
Depends On
None.
Decisions Made
| Decision | Rationale |
|---|---|
| Use modesetting Xorg driver instead of nvidia DDX | NVIDIA DDX produces NULL metamode without physical display handshake. Modesetting uses DRM/KMS directly, which detects DP-2 as connected via hot-plug detect pin in cable. |
| Accept software encoding (libx264) for now | Gets streaming working. NVENC requires either nvidia DDX (broken headless) or a dummy plug. Phase 2 addresses this. |
| Keep EDID configmap in place | Not currently used by modesetting (it reads real EDID from DP-2), but useful for future true-headless work with dummy plug or virtual display. |
| Forgejo is primary platform, GitHub is mirror | Origin remote is Forgejo. GitHub has legacy issues. |
Phases
Phase 1: Headless display fix (modesetting driver)
- Goal: Get Xorg + Sunshine working without a powered-on monitor
- Owner: Main session
- Repo: forgejo_admin/palworld-server
- Forgejo Issue: forgejo_admin/palworld-server #1
- Status: In progress (code done, PR needed)
- Steps:
Phase 2: Hardware encoding (NVENC)
- Goal: Replace libx264 software encoding with NVENC for lower latency and CPU usage
- Owner: Main session
- Repo: forgejo_admin/palworld-server
- Status: Not started
- Options:
Phase 3: True headless (remove DP-2 cable dependency)
- Goal: Streaming works with zero cables connected to GPU
- Owner: Main session
- Repo: forgejo_admin/palworld-server
- Status: Not started
- Current modesetting approach only works because DP-2 cable is plugged in. Need either dummy plug or software virtual display.
Phase 4: Documentation update
- Goal: README reflects remote-only workflow, no host package references
- Owner: Main session
- Repo: forgejo_admin/palworld-server
- Status: Not started
- Steps:
Key Files
| Phase | File | Repo | Change |
|---|---|---|---|
| 1 | terraform/main.tf | palworld-server | xorg configmap: modesetting driver, EDID volume |
| 4 | README.md | palworld-server | Remote-only docs, remove host package refs |
Verification
- [ ] Phase 1:
kubectl logs -c sunshineshows encoder found, no Fatal errors - [ ] Phase 1:
kubectl logs -c xorgshows valid mode (not NULL) - [ ] Phase 2: Sunshine logs show NVENC/nvenc encoder
- [ ] Phase 3: Works with all cables unplugged from GPU
- [ ] Phase 4: README has no references to host packages
- [ ] End-to-end: Moonlight connects from laptop, Steam visible, Palworld launches
Next Plan Seeds
- Game auto-launch: Start Palworld automatically when Moonlight connects
- Save backup automation: Scheduled world save backups to S3/MinIO
- Multi-game support: Generalize client pod for other Steam games
Related
project-pal-e-world— project page