Plan: Headless Game Streaming Server

plan-2026-03-08-headless-game-streaming Plan

plan deprecated

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 sunshine shows encoder found, no Fatal errors
  • [ ] Phase 1: kubectl logs -c xorg shows 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
  • project-pal-e-world — project page