Sunshine + Moonlight 公司环境运维手册

文档性质:公司办公机(zbc-OptiPlex-7080)实际部署现状与运维手册 维护人:zbc 最后更新:2026-06-30(review 校正:账号存储位置、conf 实际内容、脚本反馈方式) 与参考文档关系:本文是 实际部署快照 + 运维操作手册;理论/教程见同目录下 Sunshine+Moonlight投屏实践/那份文档的环境信息有多处与本机不符,照搬会踩坑,差异见第 5 节)。


0. 一句话现状

Sunshine 服务端(v2026.516.143833)已在本机部署并运行,Wayland + KMS 捕屏 + Intel VAAPI H.264 硬件编码,systemd 用户级服务、开机自启已就绪。客户端(Moonlight)用 10.221.118.99 连接,编码器必须选 H.264


1. 环境基线(固定事实,变更时更新本节)

项目
主机名zbc-OptiPlex-7080
操作系统Ubuntu 22.04.5 LTS (jammy)
glibc2.35-0ubuntu3.13(⚠️ 决定只能用 ubuntu-22.04 的 deb,见第 5 节)
桌面环境GNOME + WaylandXDG_SESSION_TYPE=wayland,非 X11)
GPUIntel UHD Graphics 630 (CometLake-S GT2)
VAAPI 驱动Intel iHD 22.3.1(intel-media-va-driver
DRM 设备/dev/dri/renderD128(属 render 组,且有 logind ACL user:zbc:rw-
主网卡eno1
显示器DP-1(XWAYLAND0, 1440×2560 竖屏)+ DP-2(XWAYLAND1, 2560×1440 横屏)

动态值(每次维护前用附录 A 脚本重新采集):局域网 IP、服务状态、端口监听。


2. 当前部署状态快照(2026-06-29 19:58 CST)

项目
Sunshine 版本2026.516.143833(amd64)
安装来源包sunshine-ubuntu-22.04-amd64.deb(10,454,170 B,本地存于 ~/sunshine.deb
二进制/usr/bin/sunshine
Linux capabilitiescap_sys_admin,cap_sys_nice=p(KMS 捕屏必需 CAP_SYS_ADMIN,安装时 postinst 自动设)
运行用户/组zbc,已加入 video,render,input
捕屏方式kms(Wayland 适配)
编码器vaapi,设备 /dev/dri/renderD128
可用编码仅 H.264 (h264_vaapi) ✅ ;HEVC/AV1 本机硬件不支持(日志会报错,属正常探测)
systemd 服务~/.config/systemd/user/sunshine.serviceactive (running)enabled
自启目标graphical-session.target.wants(登录图形会话即自启)
监听端口 (TCP)47984(HTTPS认证) / 47989(HTTP基准) / 47990(Web UI) / 48010(RTSP)
UDP 流端口47998(视频) / 47999(控制) / 48000(音频) / 48002(麦克风)
mDNS (Avahi)已广播,主机名 zbc-OptiPlex-7080,Moonlight 可自动发现
局域网 IP10.221.118.99/19(eno1,办公内网,DHCP 可能变化)
客户端可达性Moonlight 客户端须在 同一 10.221.x.x/19 网段
当前投屏屏output_name = 1 → DP-2 横屏(~/sunshine-screen.sh status 查实时值;切换见 §3.3)

启动成功的关键日志标志(排查时对照):

Info: config: 'capture' = kms
Info: Found monitor for DRM screencasting / Found connector ID [95]
Info: Found H.264 encoder: h264_vaapi [vaapi]
Info: Configuration UI available at [https://localhost:47990]
Info: Avahi service zbc-OptiPlex-7080 successfully established.

3. 关键文件清单

路径用途维护说明
~/.config/sunshine/sunshine.conf主配置(捕屏/编码/端口/安全)改后须 restart 服务
~/.config/sunshine/credentials/TLS 自签名证书 (cacert.pem/cakey.pem),不含账号Web UI 的 HTTPS 证书
~/.config/sunshine/sunshine_state.jsonWeb UI 账号(用户名 + 加盐 SHA256 哈希)+ 唯一 ID删它可重置账号密码(见 §4.3)
~/.config/sunshine/apps.jsonMoonlight 可见的应用/桌面列表Web UI → Applications 维护
~/.config/sunshine/sunshine.log历史日志文件实时排查优先用 journalctl
~/.config/systemd/user/sunshine.servicesystemd 用户服务单元改后须 daemon-reload
~/sunshine.deb安装包(22.04 版)备份便于重装/离线恢复

3.1 当前 sunshine.conf 实际内容

# 系统:Ubuntu 22.04, Intel UHD 630, Wayland (GNOME)
# 注:Web UI 保存后会按字母序重排字段、省略默认值;以下为磁盘实际内容
adapter_name = /dev/dri/renderD128   # VAAPI GPU 设备
address_family = both
capture = kms                        # Wayland 必须用 kms(不能用 x11)
encoder = vaapi
fec_percentage = 15                  # 前向纠错 15%
lan_encryption_mode = disabled       # 局域网关闭加密,降延迟/CPU
locale = zh
minimum_fps_target = 20
port = 47989                         # 流传输基准端口(Web UI=47990, HTTPS认证=47984, RTSP=48010)
output_name = 1                      # 投屏显示器 monitor 索引(0=DP-1竖, 1=DP-2横;详见 §3.3)
# origin_web_ui_allowed / upnp 未显式写入 = 取默认值(lan / disabled)

3.2 当前 sunshine.service 实际内容(Wayland 版环境变量)

[Unit]
Description=Sunshine self-hosted game stream host
After=network.target graphical-session.target
Wants=graphical-session.target
 
[Service]
ExecStart=/usr/bin/sunshine
Environment=XDG_RUNTIME_DIR=/run/user/1000
Environment=WAYLAND_DISPLAY=wayland-0
Environment=DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus
Environment=LIBVA_DRIVER_NAME=iHD
Restart=on-failure
RestartSec=5s
 
[Install]
WantedBy=graphical-session.target

Wayland 与 X11 的本质区别:服务里不要DISPLAY / XAUTHORITY(那是 X11 思路),改为 XDG_RUNTIME_DIR + WAYLAND_DISPLAY + DBUS_SESSION_BUS_ADDRESS(音频走 PipeWire 要 DBUS)。

3.3 双屏切换(DP-1 竖屏 / DP-2 横屏)

本机双显示器:DP-1(1440×2560 竖屏)/ DP-2(2560×1440 横屏)。Sunshine 一次只投一块屏,Wayland+KMS 下 per-app output-name 不生效(官方 by-design),只能用全局 output_name 切屏。

⚠️ output_name 的值用 monitor 索引(0/1),不是 connector_id、也不是名字!(实测踩坑)

写法结果
output_name = 1(monitor 索引)正解,切到 DP-2(connector 112)
output_name = 0(monitor 索引)✅ DP-1(connector 95)
output_name = 112(DRM connector_id)Couldn't find monitor [112],服务失去编码器
output_name = DP-2(名字字符串)Couldn't find monitor [23172],服务失去编码器

索引依据:Web UI「Configuration → Audio/Video → 显示器Id」字段 + 启动日志 Info: Monitor 0 is DP-1 / Monitor 1 is DP-2踩坑后果:写错值会导致 Video failed to find working encoder,投屏黑屏。恢复方法:删掉 conf 里 output_name 行 → restart(回默认 DP-1)。

切换方式(三选一,每次都会 restart + Moonlight 重连一次):

方式操作备注
键盘快捷键 ⭐Ctrl+Alt+H=横屏 / Ctrl+Alt+V=竖屏两屏通用,Moonlight 能传,已绑 dconf
桌面快捷方式双击 ~/桌面/Sunshine-切横屏.desktop / 切竖屏.desktop(桌面目录是中文 ~/桌面,非 ~/Desktopding 扩展只在主屏 DP-1 显示;新 .desktop 需 gio set <f> metadata::trusted true 才可执行
命令bash ~/sunshine-screen.sh h(横)/ v(竖)/ statusSSH/终端

关键文件:

  • ~/sunshine-screen.sh:切换脚本(改 conf 的 output_name + restart + zenity 弹窗反馈,notify-send 兜底)。脚本内必须 export XDG_RUNTIME_DIR + DBUS_SESSION_BUS_ADDRESS,否则 .desktop/快捷键启动时弹窗发不出
  • ~/桌面/Sunshine-切横屏.desktop / ~/桌面/Sunshine-切竖屏.desktop:桌面快捷方式(也注册到 ~/.local/share/applications/;本机桌面目录是 ~/桌面
  • dconf 快捷键:org.gnome.settings-daemon.plugins.media-keys.custom-keybindings/custom0,custom1

4. 日常运维命令

所有命令均为 systemd 用户级不需要 sudo(除特别标注)。

4.1 服务启停 / 状态 / 日志

# 状态
systemctl --user status sunshine.service
systemctl --user is-active sunshine.service        # active / inactive
 
# 启动 / 停止 / 重启
systemctl --user start   sunshine.service
systemctl --user stop    sunshine.service
systemctl --user restart sunshine.service
 
# 开机自启 开/关
systemctl --user enable  sunshine.service
systemctl --user disable sunshine.service
 
# 实时日志(排查首选)
journalctl --user -u sunshine.service -f
# 最近 100 行
journalctl --user -u sunshine.service -n 100 --no-pager
# 只看编码器/捕屏/错误
journalctl --user -u sunshine.service --since "10 min ago" --no-pager \
  | grep -iE "encoder|kms|capture|error|failed|vaapi"

4.2 改配置后生效

# 改了 sunshine.service → 先 daemon-reload
systemctl --user daemon-reload
systemctl --user restart sunshine.service
# 改了 sunshine.conf → 直接 restart
systemctl --user restart sunshine.service

4.3 重置 Web UI 账号密码

# ⚠️ 账号存在 sunshine_state.json,不是 credentials/(那里只有 TLS 证书,删了也重置不了密码)
rm -f ~/.config/sunshine/sunshine_state.json
systemctl --user restart sunshine.service
# 再访问 https://localhost:47990 重新设置用户名/密码

4.4 撤销某客户端配对

Web UI → Troubleshooting → 找到设备 → Unpair,然后必须重启服务才真正生效(已修复安全漏洞 GHSA-v8gw-jw28-v55m,不重启被撤销设备仍可免 PIN 重连):

systemctl --user restart sunshine.service

4.5 重新查询当前局域网 IP(IP 变了就告诉客户端新地址)

ip -4 addr show eno1 | grep inet

4.6 验证 VAAPI 编码能力(怀疑编码问题时)

sg render -c 'LIBVA_DRIVER_NAME=iHD vainfo --display drm --device /dev/dri/renderD128' \
  | grep -E "Driver|H264.*Enc|HEVC.*Enc"
# 期望含:VAProfileH264High : VAEntrypointEncSliceLP

4.7 完整卸载(如需)

systemctl --user disable --now sunshine.service
sudo apt remove --purge sunshine -y
rm -rf ~/.config/sunshine ~/.config/systemd/user/sunshine.service
systemctl --user daemon-reload

4.8 防火墙(本机 UFW 当前未强制启用;若启用则需放行)

sudo ufw allow 47984,47989,47990,48010/tcp
sudo ufw allow 47998,47999,48000,48002/udp
sudo ufw allow 5353/udp        # mDNS 自动发现

5. ⚠️ 与参考文档《Sunshine+Moonlight投屏实践》的差异(防踩坑)

参考文档(Sunshine+Moonlight投屏实践/)是 2026-06-14 的实践记录,但其声明的环境与本机现状多处不符,照搬必踩坑:

项目参考文档声称本机实际后果 / 对策
操作系统Ubuntu 24.04Ubuntu 22.04.5 (glibc 2.35)ubuntu-24.04 的 deb 会因 libc6 (>=2.38) 失败 → 必须用 ubuntu-22.04
会话类型X11 (Xorg)Wayland不能用 capture=x11 → 用 capture=kms
DisplayDISPLAY=:1不适用(Wayland)服务去掉 DISPLAY/XAUTHORITY,改 Wayland 三件套
XAUTHORITY/run/user/1000/gdm/Xauthority不存在同上
局域网 IP192.168.3.10410.221.118.99客户端连旧 IP 不通 → 用 ip -4 addr show eno1 查实时值
VAAPI 驱动版本intel-media-va-driver 24.1.022.3.1功能一致,版本号旧但可用
Sunshine 版本v2026.516.143833✅ 一致
服务文件自带 DISPLAY/XAUTHORITY自带 WAYLAND_DISPLAY/DBUS本机已改为 Wayland 版
编码器H.264(推荐)仅 H.264 可用HEVC/AV1 硬件不支持,客户端必须选 H.264

结论:参考文档适合读「原理 / 安装思路 / 外网方案对比 / 性能调优建议」,但所有具体环境值(OS、会话、IP、捕屏方式、服务环境变量)以本手册第 2、3 节为准


6. 故障排查

6.1 Moonlight 连接黑屏 / 抓不到画面

  1. 查日志有无 Found monitor for DRM screencasting —— 没有则 KMS 未抓到屏;
  2. 确认桌面在运行(loginctl 看 session 为 wayland 且 active);
  3. 重启服务:systemctl --user restart sunshine.service
  4. 实在不行临时降级排查:sunshine.confcapture=wayland(走 PipeWire portal,需 GNOME 屏幕共享授权)或切 Xorg 会话。

6.2 客户端连不上 / 扫不到主机

  1. 服务是否在跑:systemctl --user is-active sunshine.service
  2. 端口是否监听:ss -tln | grep -E '47984|47989|47990'
  3. 客户端是否在同一 10.221.x.x/19 网段(办公内网隔离最常见);
  4. IP 是否变了:ip -4 addr show eno1,客户端用最新 IP;
  5. mDNS:日志应有 Avahi service ... established,没有则手动输入 IP。

6.3 编码器相关错误

  • Encoder [vaapi] failed / Couldn't find any working encoder:检查 /dev/dri/renderD128 权限与 getcap /usr/bin/sunshine(应含 cap_sys_admin);
  • Could not open codec [hevc_vaapi] / [av1_vaapi]正常,本机不支持,忽略;
  • 临时回退:sunshine.confencoder = software(CPU 软编,延迟高,仅排障用)。

6.4 无声音

日志 grep -i audio;确认服务带 DBUS_SESSION_BUS_ADDRESS(PipeWire 走 DBUS);本机 ~/.config/systemd/user/sunshine.service 已配。

6.5 码率上不去 / 画面偏糊(Intel UHD 630 VAAPI 已知行为)

iHD 在 LP 编码模式 + CQP 下,实际码率常远低于客户端请求值(如 1440p 只给 ~15Mbps)。对策:在 Moonlight 客户端把码率滑块调高(1440p 建议 40–60 Mbps);日志可见 Using LP encoding mode 属预期,无法完全消除。

6.6 重启服务后组权限不生效

本机 systemd --user 实例是登录时启动,usermod 加的新组要重新登录才进进程 supplementary groups。但本机靠 renderD128logind ACL (user:zbc:rw-) 仍可访问 GPU,故一般无需重新登录即可正常编码。


7. 已知限制

  1. 仅 H.264 编码:UHD 630 不支持 HEVC/AV1 硬件编码,客户端必须选 H.264,否则握手失败。
  2. LP 模式码率偏低:见 6.5,靠客户端调高码率缓解。
  3. 办公内网 IP 动态10.221.118.99 为 DHCP,换网/换工位后用 ip -4 addr show eno1 重新确认。
  4. 跨网段/外网不可直连:办公网有隔离,客户端须同网段;外网访问需走参考文档 04 的 Tailscale/ZeroTier/WireGuard(frpc 不推荐用于实时串流)。
  5. sudo 需密码:本环境 sudo 非免密,安装/usermod/防火墙类操作需人工输密码。

8. 变更记录

日期变更操作人
2026-06-29初始部署:装 Sunshine 22.04 包,加 video/render/input 组,配 capture=kms + Wayland 服务,开机自启,验证 KMS 捕屏 + H.264 VAAPI 就绪zbc
2026-06-29双屏切换落地:确认 output_name 用 monitor 索引(0=DP-1, 1=DP-2),不是 connector_id(112)/名字(DP-2)(两者均致 Couldn't find monitor 黑屏);建 ~/sunshine-screen.sh + 桌面快捷方式 + Ctrl+Alt+H/V 快捷键zbc

后续每次变更(升级版本、改配置、换网、加客户端)在此追加一行,并更新第 2 节快照。


附录 A. 一键状态采集脚本(维护本文档前先跑它拿最新值)

把下面整段存为 ~/sunshine_status.shbash ~/sunshine_status.sh,输出可直接对照/填回第 2 节:

#!/usr/bin/env bash
echo "HOSTNAME : $(hostname)"
echo "OS       : $(lsb_release -ds 2>/dev/null) / libc6=$(dpkg -l libc6 2>/dev/null | awk '/^ii/{print $3}')"
echo "SESSION  : ${XDG_SESSION_TYPE:-unknown}"
echo "SUN_VER  : $(dpkg -l sunshine 2>/dev/null | awk '/^ii/{print $3}')"
echo "CAPS     : $(getcap /usr/bin/sunshine 2>/dev/null)"
echo "GROUPS   : $(id zbc 2>/dev/null | grep -oE '\((video|render|input)\)' | tr -d '()' | tr '\n' ',')"
echo "IP(eno1) : $(ip -4 addr show eno1 2>/dev/null | grep inet | awk '{print $2}')"
echo "DRM      : $(ls /dev/dri/renderD* 2>/dev/null | tr '\n' ' ')"
echo "SERVICE  : $(systemctl --user is-active sunshine.service 2>/dev/null) / enabled=$(systemctl --user is-enabled sunshine.service 2>/dev/null)"
echo "OUTPUT   : $(grep -i '^output_name' ~/.config/sunshine/sunshine.conf 2>/dev/null || echo '(未设→默认DP-1)')"
echo "PORTS(TCP): $(ss -tlnH 2>/dev/null | grep -oE ':(47984|47989|47990|48010) ' | tr -d ' :' | sort -u | tr '\n' ',')"
echo "VAAPI    : $(sg render -c 'LIBVA_DRIVER_NAME=iHD vainfo --display drm --device /dev/dri/renderD128 2>/dev/null' | grep -m1 'Driver version')"
echo "CHECK_AT : $(date '+%Y-%m-%d %H:%M %Z')"

附录 B. 参考文档位置