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) |
| glibc | 2.35-0ubuntu3.13(⚠️ 决定只能用 ubuntu-22.04 的 deb,见第 5 节) |
| 桌面环境 | GNOME + Wayland(XDG_SESSION_TYPE=wayland,非 X11) |
| GPU | Intel 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 capabilities | cap_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.service,active (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 可自动发现 |
| 局域网 IP | 10.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.json | Web UI 账号(用户名 + 加盐 SHA256 哈希)+ 唯一 ID | 删它可重置账号密码(见 §4.3) |
~/.config/sunshine/apps.json | Moonlight 可见的应用/桌面列表 | Web UI → Applications 维护 |
~/.config/sunshine/sunshine.log | 历史日志文件 | 实时排查优先用 journalctl |
~/.config/systemd/user/sunshine.service | systemd 用户服务单元 | 改后须 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.targetWayland 与 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(桌面目录是中文 ~/桌面,非 ~/Desktop) | ding 扩展只在主屏 DP-1 显示;新 .desktop 需 gio set <f> metadata::trusted true 才可执行 |
| 命令 | bash ~/sunshine-screen.sh h(横)/ v(竖)/ status | SSH/终端 |
关键文件:
~/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.service4.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.service4.5 重新查询当前局域网 IP(IP 变了就告诉客户端新地址)
ip -4 addr show eno1 | grep inet4.6 验证 VAAPI 编码能力(怀疑编码问题时)
sg render -c 'LIBVA_DRIVER_NAME=iHD vainfo --display drm --device /dev/dri/renderD128' \
| grep -E "Driver|H264.*Enc|HEVC.*Enc"
# 期望含:VAProfileH264High : VAEntrypointEncSliceLP4.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-reload4.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.04 | Ubuntu 22.04.5 (glibc 2.35) | 装 ubuntu-24.04 的 deb 会因 libc6 (>=2.38) 失败 → 必须用 ubuntu-22.04 包 |
| 会话类型 | X11 (Xorg) | Wayland | 不能用 capture=x11 → 用 capture=kms |
| Display | DISPLAY=:1 | 不适用(Wayland) | 服务去掉 DISPLAY/XAUTHORITY,改 Wayland 三件套 |
XAUTHORITY | /run/user/1000/gdm/Xauthority | 不存在 | 同上 |
| 局域网 IP | 192.168.3.104 | 10.221.118.99 | 客户端连旧 IP 不通 → 用 ip -4 addr show eno1 查实时值 |
| VAAPI 驱动版本 | intel-media-va-driver 24.1.0 | 22.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 连接黑屏 / 抓不到画面
- 查日志有无
Found monitor for DRM screencasting—— 没有则 KMS 未抓到屏; - 确认桌面在运行(
loginctl看 session 为wayland且 active); - 重启服务:
systemctl --user restart sunshine.service; - 实在不行临时降级排查:
sunshine.conf改capture=wayland(走 PipeWire portal,需 GNOME 屏幕共享授权)或切 Xorg 会话。
6.2 客户端连不上 / 扫不到主机
- 服务是否在跑:
systemctl --user is-active sunshine.service; - 端口是否监听:
ss -tln | grep -E '47984|47989|47990'; - 客户端是否在同一
10.221.x.x/19网段(办公内网隔离最常见); - IP 是否变了:
ip -4 addr show eno1,客户端用最新 IP; - 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.conf设encoder = 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。但本机靠 renderD128 的 logind ACL (user:zbc:rw-) 仍可访问 GPU,故一般无需重新登录即可正常编码。
7. 已知限制
- 仅 H.264 编码:UHD 630 不支持 HEVC/AV1 硬件编码,客户端必须选 H.264,否则握手失败。
- LP 模式码率偏低:见 6.5,靠客户端调高码率缓解。
- 办公内网 IP 动态:
10.221.118.99为 DHCP,换网/换工位后用ip -4 addr show eno1重新确认。 - 跨网段/外网不可直连:办公网有隔离,客户端须同网段;外网访问需走参考文档 04 的 Tailscale/ZeroTier/WireGuard(frpc 不推荐用于实时串流)。
- 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.sh 后 bash ~/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. 参考文档位置
- 教程/原理/外网方案:
/home/zbc/pangu/GeneralAndroid/爬取的文章/Sunshine+Moonlight投屏实践/(01_Linux端...02_Windows端...03_进阶配置04_外网访问方案README) - 投屏方案横向对比:
/home/zbc/pangu/GeneralAndroid/爬取的文章/Linux投屏到Windows局域网低延迟方案全面对比.md - 官方文档:https://docs.lizardbyte.dev/projects/sunshine/latest/
- 安装包下载:https://github.com/LizardByte/Sunshine/releases (本机用
sunshine-ubuntu-22.04-amd64.deb) - Moonlight 客户端:https://github.com/moonlight-stream/moonlight-qt/releases