Sunshine Docker Compose 部署指南
Sunshine v2026.914.233613 的 Docker Compose 部署全流程实测:镜像 tag 选型(OS 后缀陷阱)、GameStream 端口契约、配置目录属主 uid 1001 陷阱、首启凭据初始化、Moonlight PIN 配对 API 闭环、持久化数据布局、6 条排障实录与反代/升级/卸载(实测环境 2026-11-03)。
Sunshine Docker Compose 部署指南
目标:用 Docker Compose 把 Sunshine(v2026.914.233613)部署为游戏串流主机端,覆盖镜像选型、端口契约、首启凭据初始化、Moonlight PIN 配对全流程、持久化与常见故障。本文所有回显均为 Ubuntu 24.04.5 LTS 宿主机上逐条实测照抄;宿主机无 GPU 驱动,串流画面链路不可用,但 Web UI / REST API / 配对 / 配置面完整实测通过(这正是无 GPU 服务器评估 Sunshine 管理面的可行形态)。
1. 环境信息
| 项目 | 值 |
|---|---|
| 宿主机 OS | Ubuntu 24.04.5 LTS(x86_64,内核 7.0.0-38-generic) |
| CPU / 内存 | 6 核 / 16 GB |
| GPU | 无独显驱动可用(容器未挂 /dev/dri,串流编码链路不可用,仅实测管理面) |
| Docker | 29.9.2(linux/amd64) |
| Docker Compose | v5.6.1 |
| 镜像 | ghcr.io/lizardbyte/sunshine:v2026.914.233613-ubuntu-24.04 |
| 容器基座 | Ubuntu 24.04.4 LTS |
| 部署栈名 | sunshine-2026.914.233613-stack |
| 管理端口 | 宿主 8135 → 容器 47990(Web UI / API,HTTPS) |
| 示例账号 | admin / q1w2e3r4 |
⚠️ 硬件前置:Sunshine 的串流主体功能要求主机具备 GPU 编码能力(NVENC/AMF/QSV/VAAPI 任一)与可抓取的显示输出。纯无 GPU 服务器按本文部署后只能使用 Web UI/API/配置面,客户端连接会失败——这是产品形态使然,不是部署错误。
2. 版本选型依据
- 最新稳定版 v2026.914.233613(GitHub Releases,发布于 2026-09-15,查询 2026-11-03)。版本号为日期式
YYYY.MDD.HHMMSS。 - ghcr.io 镜像 tag 与发布 tag 对应,但必须带 OS 后缀:
v2026.914.233613-ubuntu-24.04。实测裸latesttag 的构建缺运行时依赖,启动即崩:
$ docker run --rm --entrypoint sunshine ghcr.io/lizardbyte/sunshine:latest --version
sunshine: error while loading shared libraries: libva.so.2: cannot open shared object file: No such file or directory
- 带后缀 tag 启动正常,
--version回显:
$ docker run --rm --entrypoint sunshine ghcr.io/lizardbyte/sunshine:v2026.914.233613-ubuntu-24.04 --version
[2026-11-03 02:12:16.482]: Info: Sunshine version: 2026.914.233613 commit: 63d35f702ee9e362e43263742981836ec0710384
[2026-11-03 02:12:16.482]: Info: Package Publisher: LizardByte
[2026-11-03 02:12:16.482]: Info: Publisher Website: https://app.lizardbyte.dev
[2026-11-03 02:12:16.482]: Info: Get support: https://app.lizardbyte.dev/support
结论:生产固定 v2026.914.233613-ubuntu-24.04;nightly 通道 tag 为 nightly-ubuntu-24.04(ghcr 标签列表,查询 2026-11-03),仅测试环境用。
3. 部署前置
3.1 端口契约
| 容器端口 | 宿主端口(本例) | 用途 |
|---|---|---|
| 47990/tcp | 8135 | Web UI / REST API(HTTPS) |
| 47984/tcp | 47984 | GameStream HTTPS(TLS+客户端证书) |
| 47989/tcp | 47989 | GameStream HTTP(serverinfo/pair 握手) |
| 48010/tcp | 48010 | RTSP 会话协商 |
| 47998/47999/48000/udp | 同名 | 控制/视频/音频流 |
Web UI 端口恒为配置键 port(默认 47989)+ 1 = 47990(实测 port=48999 时日志即提示 UI 在 49000,见 C1)。
3.2 配置目录属主(关键前置)
镜像内运行用户为 lizard,uid/gid = 1001(不是常见的 1000):
$ docker run --rm --entrypoint bash ghcr.io/lizardbyte/sunshine:v2026.914.233613-ubuntu-24.04 -c 'id; grep lizard /etc/passwd'
uid=0(root) gid=0(root) groups=0(root)
lizard:x:1001:1001::/home/lizard:/bin/bash
服务进程(PID 1)实际以 lizard 运行——二进制启动时自行降权,且不应用 PUID/PGID 环境变量(实测目标 uid 恒为 1001):
$ docker exec sunshine-2026.914.233613-host bash -lc 'ps -o user,pid,cmd -p 1; id'
USER PID CMD
lizard 1 /usr/bin/sunshine
uid=1001(lizard) gid=1001(lizard) groups=1001(lizard)
挂载的配置目录若属主不对,首启会因无法创建 credentials/ 而崩溃循环(见 9.1)。部署前先建目录并授权:
$ mkdir -p config && chown 1001:1001 config
$ ls -ld config
drwxr-xr-x 3 1001 1001 4096 Nov 3 10:37 config
4. compose 编排
docker-compose.yml(全文实测运行通过):
name: sunshine-2026.914.233613-stack
services:
sunshine:
image: ghcr.io/lizardbyte/sunshine:v2026.914.233613-ubuntu-24.04
container_name: sunshine-2026.914.233613-host
hostname: sunshine-host
environment:
PUID: "1000"
PGID: "1000"
TZ: Asia/Shanghai
ports:
- "8135:47990" # Web UI / HTTP API(HTTPS)
- "47984:47984" # GameStream HTTPS(TLS+客户端证书)
- "47989:47989" # GameStream HTTP(serverinfo/pair)
- "48010:48010" # RTSP 视频流(TCP)
- "47998:47998/udp" # 控制通道(UDP)
- "47999:47999/udp" # 视频流(UDP)
- "48000:48000/udp" # 音频流(UDP)
volumes:
- ./config:/home/lizard/.config/sunshine
networks:
sunshine-net:
ipv4_address: 10.89.62.10
restart: unless-stopped
networks:
sunshine-net:
driver: bridge
ipam:
config:
- subnet: 10.89.62.0/24
gateway: 10.89.62.1
要点说明:
name:显式固定栈名,避免目录名变化导致项目漂移。PUID/PGID环境变量是镜像默认值残留,entrypoint 不会应用它们(实测 uid 仍为 1001),真正要做的是宿主目录属主1001:1001。- 带 GPU 的游戏主机部署应追加设备映射与显示相关挂载(
devices: ["/dev/dri"]、X11/Wayland socket 等),本文无 GPU 形态不演示。 - 自定义网络固定子网与容器 IP(
10.89.62.10),便于防火墙规则与 mDNS 替代发现。
5. 启动与首验
$ docker compose up -d
[+] Running 2/2
✔ Network sunshine-2026914233613-stack_sunshine-net Created
✔ Container sunshine-2026.914.233613-host Started
$ docker compose ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
sunshine-2026.914.233613-host ghcr.io/lizardbyte/sunshine:v2026.914.233613-ubuntu-24.04 "/usr/bin/sunshine" sunshine 16 minutes ago Up 5 minutes 0.0.0.0:47984->47984/tcp, ... 0.0.0.0:8135->47990/tcp, ...
首启日志(无 GPU/显示环境下的真实形态——编码链路 fatal,但管理面照常起来):
$ docker logs sunshine-2026.914.233613-host 2>&1 | head -30
[2026-11-03 02:12:44.766]: Warning: Couldn't find /dev/dri, kmsgrab won't be enabled
[2026-11-03 02:12:44.766]: Error: Couldn't find any of the following libraries: [libavahi-common.so.3, libavahi-common.so]
[2026-11-03 02:12:44.766]: Info: Install the Avahi libraries for mDNS support
[2026-11-03 02:12:44.766]: Error: Couldn't find any of the following libraries: [libavahi-client.so.3, libavahi-client.so]
[2026-11-03 02:12:44.766]: Info: Install the Avahi libraries for mDNS support
[2026-11-03 02:12:44.767]: Info: Sunshine version: 2026.914.233613 commit: 63d35f702ee9e362e43263742981836ec0710384
[2026-11-03 02:12:44.767]: Info: Package Publisher: LizardByte
[2026-11-03 02:12:44.767]: Info: Publisher Website: https://app.lizardbyte.dev
[2026-11-03 02:12:44.767]: Info: Get support: https://app.lizardbyte.dev/support
[2026-11-03 02:12:44.767]: Warning: [portalgrab] Failed to connect to dbus. Cannot enumerate displays, returning empty list.
[2026-11-03 02:12:44.767]: Warning: Gamepad auto is disabled due to gamepads.virtualhid-not-available
[2026-11-03 02:12:44.767]: Warning: Gamepad generic is disabled due to gamepads.virtualhid-not-available
[2026-11-03 02:12:44.767]: Warning: Gamepad 360W is disabled due to gamepads.virtualhid-not-available
[2026-11-03 02:12:44.767]: Warning: Gamepad DS5 is disabled due to gamepads.virtualhid-not-available
[2026-11-03 02:12:44.767]: Warning: Gamepad DualSense is disabled due to gamepads.virtualhid-not-available
[2026-11-03 02:12:44.767]: Warning: Gamepad Steam is disabled due to gamepads.virtualhid-not-available
[2026-11-03 02:12:44.767]: Fatal: Unable to find display or connected
[2026-11-03 02:12:44.767]: Fatal: Unable to find display or connected
[2026-11-03 02:12:44.767]: Fatal: Unable to find display or connected
[2026-11-03 02:12:44.767]: Fatal: Unable to find display or connected
[2026-11-03 02:12:44.767]: Fatal: No available encoder backends: [nvenc, vulkan, vaapi, software]
[2026-11-03 02:12:44.767]: Info: No available encoder backends: [nvenc, vulkan, vaapi, software]
[2026-11-03 02:12:44.767]: Info:
[2026-11-03 02:12:44.767]: Info: Open the Web UI to set your new username and password and getting started
[2026-11-03 02:12:44.767]: Info:
[2026-11-03 02:12:44.767]: Info: Configuration UI available at [https://localhost:47990]
[2026-11-03 02:12:45.030]: Info: Starting main loop
[2026-11-03 02:12:45.030]: Error: System tray is not initialized
[2026-11-03 02:12:45.030]: Info: Main loop has exited
解读:
No available encoder backends: [nvenc, vulkan, vaapi, software]——四类后端探测全失败(无 GPU/显示),串流不可用;Open the Web UI to set your new username and password——无凭据时等待首启初始化;Configuration UI available at [https://localhost:47990]——管理面照常监听;System tray is not initialized/Main loop has exited——无头环境托盘不可用,主循环退出但 Web UI 线程存活(服务仍在跑,见 9.5 对 restart 的影响)。
宿主端口监听核验:
$ ss -ltn | grep -E ':(8135|47984|47989|48010)\s'
LISTEN 0 4096 0.0.0.0:48010 0.0.0.0:*
LISTEN 0 4096 0.0.0.0:47989 0.0.0.0:*
LISTEN 0 4096 0.0.0.0:47984 0.0.0.0:*
LISTEN 0 4096 0.0.0.0:8135 0.0.0.0:*
GameStream 握手面(47989)直接可探:
$ curl -s "http://127.0.0.1:47989/serverinfo?uniqueid=00000000-0000-4000-8000-000000000000&hostaddress=10.89.62.1&protocol=128&appversion=7.1.431"
<root appversion="7.1.431.-1" GfeVersion="3.23.0.74" protocolversion="399" serverinfo="7.1.431.-1" ...>
<hostname>sunshine-host</hostname>
<localip>10.89.62.10</localip>
<state>SUNSHINE_SERVER_FREE</state>
<HttpsPort>47984</HttpsPort>
<ExternalPort>47989</ExternalPort>
...
</root>
6. 首启初始化(Web UI 配对流程实测)
浏览器打开 https://<宿主IP>:8135(自签证书需信任;未设凭据时自动跳转 /welcome 创建账号)。等价 API 流程实测如下:
$ curl -sk -o /dev/null -w "code=%{http_code} redirect=%{redirect_url}\n" https://127.0.0.1:8135/
code=307 redirect=https://127.0.0.1:8135/welcome
$ curl -sk -X POST -H 'Content-Type: application/json' \
-d '{"newUsername":"admin","newPassword":"q1w2e3r4","confirmNewPassword":"q1w2e3r4"}' \
https://127.0.0.1:8135/api/password -w " code=%{http_code}\n"
{"status":true} code=200
凭据创建后,Web UI 与 API 统一走 HTTP Basic 认证(realm 即 GameStream 主机名):
$ curl -sk -D - -o /dev/null https://127.0.0.1:8135/ | grep -iE '^HTTP|www-auth'
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Basic realm="Sunshine Gamestream Host", charset="UTF-8"
$ curl -sk -o /dev/null -w "code=%{http_code}\n" -u admin:q1w2e3r4 https://127.0.0.1:8135/
code=200
$ curl -sk -o /dev/null -w "code=%{http_code}\n" -u admin:wrongpass https://127.0.0.1:8135/
code=401
凭据落盘为 config/sunshine_state.json(密码为 salt 派生哈希,非明文):
$ cat config/sunshine_state.json
{
"username": "admin",
"salt": "6S&yMBx5gPuzi9an",
"password": "5D6BA198AD1FD6B5120FD093D7EB101FBAFCD48467181297D8A11D28DE1AFE07"
}
7. Moonlight 客户端配对实测
真实 Moonlight 客户端在 UI 上显示 4 位 PIN 并发起握手;这里用 GameStream 协议请求模拟客户端侧,完整验证主机侧配对闭环。
1)客户端发起配对(uniqueid 必须是 UUID 格式,phrase=getservercert 注册会话):
$ curl -s "http://127.0.0.1:47989/pair?uniqueid=12345678-9abc-def0-1234-56789abcdef0&phrase=getservercert&clientcert=<hex证书>&salt=<32位hex>&devicename=Pixel-8"
(该请求会挂起等待主机侧输入 PIN,最长约 2 分钟超时)
2)主机侧查看待配对请求(Web UI 的 PIN 框背后就是这组 API):
$ curl -sk -u admin:q1w2e3r4 https://127.0.0.1:8135/api/pin
{"pairings":[{"address":"10.89.62.1","id":"2b07da1c95c2735e69613739fac44279","name":"Pixel-8"}],"status":true}
3)提交 PIN 批准(pairing_id 为上一步的 id,PIN 必须恰好 4 位数字):
$ curl -sk -u admin:q1w2e3r4 -X POST -H 'Content-Type: application/json' \
-d '{"pairing_id":"2b07da1c95c2735e69613739fac44279","pin":"1234","name":"Pixel-8"}' \
https://127.0.0.1:8135/api/pin
4)等待中的客户端请求立即收到结果(paired=1 + 主机证书 hex):
<root status_code="200"><paired>1</paired><plaincert>308202543082013C...</plaincert></root>
5)待配对列表清空:
$ curl -sk -u admin:q1w2e3r4 https://127.0.0.1:8135/api/pin
{"pairings":[],"status":true}
细节:PIN 由 salt+pin 派生 AES 密钥参与后续握手,错误 PIN 不会在这一步被 API 拒绝,而是导致客户端侧校验失败、配对中断(实测用 9999 批准时 API 同样返回 paired=1,但真实客户端会报 PIN 不匹配)。完整握手完成后客户端证书登记进 sunshine_state.json,GET /api/clients/list 可见。
8. 持久化与数据布局
./config 挂载即全部状态,首启后实测:
$ ls -la config/
total 40
drwxr-xr-x 3 1001 1001 4096 Nov 3 10:37 .
-rw-r--r-- 1 1001 1001 679 Nov 3 10:28 apps.json
drwxr-xr-x 2 1001 1001 4096 Nov 3 10:28 credentials
-rw-r--r-- 1 1001 1001 0 Nov 3 10:28 sunshine.conf
-rw-r--r-- 1 1001 1001 4520 Nov 3 10:37 sunshine.log
-rw-r--r-- 1 1001 1001 116 Nov 3 10:31 sunshine.log.1
-rw-r--r-- 1 1001 1001 5070 Nov 3 10:34 sunshine.log.2
-rw-r--r-- 1 1001 1001 144 Nov 3 10:29 sunshine_state.json
(宿主上 uid 1001 无对应用户名,故显示数字;容器内同一路径显示为 lizard:lizard。)
apps.json:默认含 Desktop / Low Res Desktop / Steam Big Picture 三个流应用;credentials/:cacert.pem/cakey.pem,CN=Sunshine Gamestream Host自签证书(有效期约 40 年,Web UI 与 GameStream TLS 共用);sunshine.conf:初始为空,UI 保存配置后写入键值行(见 C1);sunshine.log(.1~.5):日志轮转保留 5 份。
备份迁移只需打包 config/ 目录(含已配对客户端与凭据)。
9. 常见问题(排障实录)
9.1 首启崩溃循环:Permission denied
现象:容器反复 Restarting (0),日志:
[2026-11-03 02:28:39.520]: Error: Failed to apply config: filesystem error: cannot create directories: Permission denied [/home/lizard/.config/sunshine/credentials/]
原因:宿主 config/ 目录属主为 root(uid 0),而镜像内用户是 uid 1001(lizard),PUID/PGID 环境变量不被 entrypoint 应用。
解决:
$ chown 1001:1001 config
$ docker compose up -d
9.2 裸 latest tag 起不来
libva.so.2: cannot open shared object file——无 OS 后缀的最小构建缺编码依赖,改用 -ubuntu-24.04 后缀 tag(见第 2 节)。
9.3 第二个实例启动报端口占用
同时跑两个 Sunshine 时后启动者 fatal 退出:
[2026-11-03 02:25:52.278]: Fatal: Couldn't bind RTSP server to port [48010], Address already in use
48010 是 RTSP 协商端口(本版本实测确认,并非文档常见的 47990 说法);同机双实例必须错开 port 与 RTSP。
9.4 mDNS 自动发现不可用
日志 Couldn't find any of the following libraries: [libavahi-common.so.3, ...]——镜像未打包 Avahi 库,Moonlight 客户端无法自动发现主机,需手动添加主机 IP(端口 47989)。功能不受影响。
9.5 无头环境 /api/restart 挂死
无 GPU/显示时主循环已退出(Main loop has exited),此时调用 POST /api/restart 会触发中断但进程无法自行收尾:
[2026-11-03 02:17:17.047]: Info: Interrupt handler called
[2026-11-03 02:17:27.048]: Fatal: 10 seconds passed, yet Sunshine's still running: Forcing shutdown
进程卡住不退出,需外部重启(docker compose restart)。带显示的正常主机不受影响。
9.6 浏览器证书告警
自签证书(CN=Sunshine Gamestream Host)触发告警属预期;长期使用建议反代挂受信证书(见第 10 节)或在客户端信任该 CA。
10. 反向代理与 HTTPS
Web UI 是 HTTPS 自签证书 + Basic 认证。如需受信证书与 443 统一入口,反代模板(nginx,配置模板非本文实测项):
server {
listen 443 ssl;
server_name sunshine.example.com;
ssl_certificate /etc/nginx/certs/sunshine.crt;
ssl_certificate_key /etc/nginx/certs/sunshine.key;
location / {
proxy_pass https://127.0.0.1:8135;
proxy_ssl_verify off;
}
}
注意:反代只服务 Web UI/API(47990);GameStream/RTSP/UDP 流媒体端口(47984/47989/48010/47998-48000)不走 HTTP 反代,需直接放通或端口映射,否则 Moonlight 能配对但无法串流。
11. 升级与回滚
升级:改 compose 中 image tag 后重新拉取并滚动更新(状态全在 config/,无损):
$ docker compose pull
Image ghcr.io/lizardbyte/sunshine:v2026.914.233613-ubuntu-24.04 Pulling
Image ghcr.io/lizardbyte/sunshine:v2026.914.233613-ubuntu-24.04 Pulled
$ docker compose up -d
回滚:image tag 改回旧版本(如 v2026.617.162634-ubuntu-24.04)再 up -d。sunshine_state.json/apps.json/credentials/ 向后兼容,回滚前备份 config/ 即可。
12. 卸载
$ docker compose down
[+] Running 2/2
✔ Container sunshine-2026.914.233613-host Removed
✔ Network sunshine-2026914233613-stack_sunshine-net Removed
$ rm -rf config/ # 连同凭据/配对/配置一并清除,先备份再删