Files
infrastructure/docs/04-PHASE4-REMOTE-GAMING.md
jazzymc 62a6267026
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Add infrastructure documentation
2026-01-18 16:57:25 +02:00

12 KiB

Phase 4: Remote Gaming (Sunshine + Moonlight)

Goal

Enable low-latency 60FPS game streaming from Nobara Linux (AMD GPU) to MacBook and Android devices, using Tailscale for optimal network pathing.


Prerequisites

  • Gaming PC: Nobara Linux with AMD GPU (VA-API support)
  • Tailscale: Installed on gaming PC (from Phase 1)
  • Clients: MacBook and Android devices with Tailscale

Architecture Overview

                    ┌─────────────────────────────────┐
                    │  Tailscale Mesh Network         │
                    │  (Encrypted, P2P when possible) │
                    └─────────────────┬───────────────┘
                                      │
         ┌────────────────────────────┼────────────────────────────┐
         │                            │                            │
         ▼                            ▼                            ▼
┌─────────────────┐        ┌─────────────────┐        ┌─────────────────┐
│ Nobara Gaming PC│        │ MacBook         │        │ Android Device  │
│ Sunshine Host   │        │ Moonlight Client│        │ Moonlight Client│
│ 100.x.x.x (TS)  │        │ 100.x.x.x (TS)  │        │ 100.x.x.x (TS)  │
│ AMD VA-API      │        │                 │        │                 │
└─────────────────┘        └─────────────────┘        └─────────────────┘

Why Tailscale for Gaming?

  • Automatic P2P connection when on same LAN
  • Encrypted tunnel when remote
  • No manual port forwarding required
  • MagicDNS for easy hostname resolution
  • NAT traversal handled automatically

Implementation Steps

Step 4.1: Install Sunshine on Nobara

Method A: Flatpak (Recommended for Nobara)

# Install via Flatpak
flatpak install flathub dev.lizardbyte.app.Sunshine

# Enable autostart
flatpak run --command=sunshine dev.lizardbyte.app.Sunshine &

Method B: Native Package

# Add Sunshine repository
sudo dnf copr enable lizardbyte/stable
sudo dnf install sunshine

# Enable service
sudo systemctl enable --now sunshine

Post-Install:

  1. Access Sunshine web UI: https://localhost:47990
  2. Set initial admin password
  3. Complete setup wizard

Step 4.2: Configure AMD VA-API Hardware Encoding

Verify AMD GPU:

# Check for AMD GPU
lspci | grep -i vga

# Verify VA-API support
vainfo

Expected output:

vainfo: VA-API version: 1.x
vainfo: Driver version: Mesa Gallium driver ...
vainfo: Supported profile and entrypoints:
        VAProfileH264Main               : VAEntrypointEncSlice
        VAProfileHEVCMain               : VAEntrypointEncSlice

Configure Sunshine for VA-API:

  1. Open Sunshine Web UI → Configuration → Video
  2. Set:
    • Encoder: vaapi
    • Adapter: /dev/dri/renderD128 (default AMD)
    • Codec: H.265/HEVC (better compression, lower latency)

Sunshine config file (~/.config/sunshine/sunshine.conf):

[video]
encoder = vaapi
adapter_name = /dev/dri/renderD128
hevc_mode = 2  # Always use HEVC when client supports

[stream]
fps = [30, 60, 120]
resolutions = [
    1920x1080,
    2560x1440,
    3840x2160
]

Step 4.3: Add Applications to Sunshine

Access: Sunshine Web UI → Applications

Recommended Applications:

Name Command Working Directory
Desktop - -
Steam Big Picture steam -bigpicture -
Lutris lutris -
Heroic Games heroic -

For specific games:

Name: Cyberpunk 2077
Command: steam steam://rungameid/1091500
Detached: ["steam"]

Step 4.4: Install Tailscale on Nobara

# Install Tailscale
curl -fsSL https://tailscale.com/install.sh | sh

# Authenticate
sudo tailscale up

# Get Tailscale IP
tailscale ip -4

Note the Tailscale IP (e.g., 100.64.x.x) - clients will connect to this.


Step 4.5: Configure Sunshine for Tailscale

Sunshine listens on all interfaces by default. For security, restrict to Tailscale:

Option A: Bind to Tailscale interface only

# ~/.config/sunshine/sunshine.conf
[network]
address_family = both
origin_address = 100.64.x.x  # Your Tailscale IP

Option B: Firewall rules (recommended)

# Allow Sunshine ports only from Tailscale
sudo firewall-cmd --permanent --zone=trusted --add-source=100.64.0.0/10
sudo firewall-cmd --permanent --zone=trusted --add-port=47984-48010/tcp
sudo firewall-cmd --permanent --zone=trusted --add-port=47998-48010/udp
sudo firewall-cmd --permanent --zone=trusted --add-port=47989/tcp  # Web UI
sudo firewall-cmd --permanent --zone=trusted --add-port=47990/tcp  # Web UI HTTPS
sudo firewall-cmd --reload

Step 4.6: Install Moonlight Clients

MacBook

# Homebrew
brew install --cask moonlight

# Or download from: https://moonlight-stream.org/

Android

  • Install "Moonlight Game Streaming" from Google Play Store

Step 4.7: Pair Moonlight with Sunshine

  1. On Moonlight client:

    • Add PC manually: Enter Tailscale IP (e.g., 100.64.x.x)
    • Or use MagicDNS hostname: nobara-pc (if enabled in Tailscale)
  2. Enter PIN:

    • Moonlight displays a 4-digit PIN
    • Enter in Sunshine Web UI → PIN Pairing
  3. Verify connection:

    • Moonlight should show your configured applications

Goal: Prioritize Nobara PC traffic to prevent bufferbloat during gaming sessions.

SSH to MikroTik:

ssh -i /root/.ssh/mikrotik_key -p 2222 unraid@192.168.31.1

Create Simple Queue for Gaming PC:

# First, find Nobara's IP (replace with actual)
# Assuming Nobara is at 192.168.31.50

# Create queue for gaming priority
/queue simple add \
    name="Gaming-Priority" \
    target=192.168.31.50 \
    max-limit=0/0 \
    priority=1/1 \
    queue=default-small/default-small \
    comment="Nobara Gaming PC Priority"

# Alternative: Use queue tree for more control
/queue tree add \
    name="Gaming-Upload" \
    parent=global \
    packet-mark=gaming-upload \
    priority=1 \
    max-limit=50M

/queue tree add \
    name="Gaming-Download" \
    parent=global \
    packet-mark=gaming-download \
    priority=1 \
    max-limit=100M

# Mark gaming traffic
/ip firewall mangle add \
    chain=prerouting \
    src-address=192.168.31.50 \
    action=mark-packet \
    new-packet-mark=gaming-upload \
    passthrough=yes

/ip firewall mangle add \
    chain=postrouting \
    dst-address=192.168.31.50 \
    action=mark-packet \
    new-packet-mark=gaming-download \
    passthrough=yes

Step 4.9: Optimize Streaming Settings

Sunshine Settings (Server):

Setting LAN Value Remote Value
Bitrate 50-80 Mbps 20-40 Mbps
FPS 60-120 60
Resolution Native 1080p
Codec HEVC HEVC

Moonlight Settings (Client):

Setting LAN Value Remote Value
Video Codec HEVC (if supported) HEVC
Frame Pacing V-Sync On
Bitrate Auto or 50+ Mbps 20 Mbps
Resolution Match display 1080p

Network Path Analysis

Tailscale P2P (Same Network):

MacBook → Router → Nobara PC
Latency: <1ms additional overhead

Tailscale Relayed (Different Network):

MacBook → Tailscale DERP → Nobara PC
Latency: ~20-50ms additional overhead

With Tailscale Direct (NAT Traversal Success):

MacBook (Office) → Internet → Home Router → Nobara PC
Latency: RTT/2 + encoding latency (~30-80ms typical)

Service Interruption Assessment

Action Risk Impact Mitigation
Install Sunshine NONE Nobara only -
Install Tailscale NONE Nobara only -
MikroTik QoS LOW May affect other traffic briefly Test during low usage
Firewall rules LOW Nobara only Can revert

Verification Checklist

  • Sunshine installed and running on Nobara
  • VA-API encoding verified: vainfo shows HEVC support
  • Tailscale running on Nobara: tailscale status
  • Sunshine Web UI accessible: https://<tailscale-ip>:47990
  • Moonlight paired successfully
  • Desktop streaming works (low latency)
  • Game streaming works at 60 FPS
  • Remote streaming works (via Tailscale from external network)
  • MikroTik QoS queue active (optional)

Troubleshooting

High Latency / Stuttering

  1. Check Tailscale connection type:

    tailscale status
    # Look for "direct" vs "relay"
    
  2. Force direct connection:

    tailscale ping <client-hostname>
    
  3. Lower bitrate in Moonlight

Encoding Errors

  1. Verify VA-API:

    sudo vainfo
    # Should show HEVC/H264 encode support
    
  2. Check Sunshine logs:

    journalctl -u sunshine -f
    # Or: ~/.config/sunshine/sunshine.log
    
  3. Fall back to software encoding:

    # sunshine.conf
    encoder = software
    

No Audio

  1. Check PulseAudio/PipeWire:

    pactl list sinks
    
  2. Set Sunshine audio sink:

    • Web UI → Audio → Select correct output

Security Considerations

  1. Tailscale-only access: Sunshine is only reachable via Tailscale network

  2. PIN pairing: Each client must be manually paired

  3. Web UI protection: Consider adding Authentik forward auth (optional)

  4. Firewall: Block Sunshine ports from non-Tailscale interfaces


Optional: Expose Sunshine Web UI via Traefik

If you want to manage Sunshine remotely via browser:

Add to Traefik dynamic.yml:

http:
  routers:
    sunshine-secure:
      rule: "Host(`sunshine.xtrm-lab.org`)"
      entryPoints:
        - https
      middlewares:
        - default-headers
        - authentik-forward-auth  # Protect with Authentik
      tls:
        certResolver: cloudflare
      service: sunshine

  services:
    sunshine:
      loadBalancer:
        servers:
          - url: "https://192.168.31.50:47990"  # Nobara IP

MikroTik hairpin NAT (if needed):

# Only if accessing from LAN via external hostname
/ip/firewall/nat add chain=srcnat \
    action=masquerade \
    src-address=192.168.31.0/24 \
    dst-address=192.168.31.50 \
    dst-port=47990 \
    protocol=tcp

Files Modified

File/System Change Backup Required
Nobara: ~/.config/sunshine/ Sunshine config No (new install)
Nobara: firewalld Allow Tailscale ports Can revert
MikroTik: Queue Gaming priority N/A
Traefik dynamic.yml (optional) Sunshine route YES

Performance Expectations

Scenario Expected Latency FPS
LAN (same network) 5-15ms 60-120
Remote (Tailscale direct) 30-60ms 60
Remote (Tailscale relay) 50-100ms 60

Dependencies for Next Phase

Phase 5 (RustDesk) provides:

  • Alternative remote access when gaming not required
  • Lower resource usage for general desktop access
  • Additional fallback for remote management