Sunshine 2026.914.233613 配置参数详解

Sunshine v2026.914.233613 配置体系全解:sunshine.conf 语法与 99 个配置键分组详解(默认值/取值范围核对源码 src/config.cpp)、四种修改方式实测与优先级、port 全家端口平移、origin_web_ui_allowed 与 CSRF 实测对照、apps.json 字段与 API CRUD 即时生效、POST /api/config 整体覆盖陷阱、生效机制与日志轮转(实测环境 2026-11-03)。

SunshineSunshine配置详解游戏串流2026/11/03

Sunshine 2026.914.233613 配置参数详解

目标:把 Sunshine 的配置体系讲透——sunshine.conf 语法与文件布局、四种修改方式(文件 / Web UI / REST API / CLI)及优先级、99 个配置键按功能分组逐项详解(默认值与取值范围均核对自 v2026.914.233613 源码 src/config.cpp)、apps.json 应用清单字段与 API 管理、配置生效机制与日志轮转、常见坑。本文所有回显均为 2026-11-03 在 Ubuntu 24.04.5 LTS 服务器上逐条实测照抄;无 GPU/无显示形态下管理面与配置面完整可用,编码链路仅做参数面说明。

1. 环境信息

项目值
服务器 OSUbuntu 24.04.5 LTS(x86_64,内核 7.0.0-38-generic)
Sunshine 版本2026.914.233613(commit 63d35f70,发布于 2026-09-15)
运行形态root 实例,配置目录 /root/.config/sunshine/
Web UI / APIhttps://localhost:47990(自签证书,curl 需 -k)
管理凭据admin / q1w2e3r4(HTTP Basic)
GameStream 端口47989(HTTP)/ 47984(HTTPS)/ 48010(RTSP)
源码核对文件src/config.cpp(默认值块 737–913 行、解析块 1626–1976 行)、src/confighttp.cpp(API 面)

前置阅读:产品与架构见 Sunshine 介绍与选型指南,服务器部署形态见 Sunshine Docker Compose 部署指南。

2. 配置体系总览

2.1 文件布局(实测)

Sunshine 的全部运行态文件落在用户配置目录(Linux 为 ~/.config/sunshine/,root 实例即 /root/.config/sunshine/)。首启后目录实测:

$ ls -la /root/.config/sunshine/
total 56
drwxr-xr-x 3 root root 4096 Nov  3 10:37 .
drwxr-xr-x 7 root root 4096 Nov  3 10:52 ..
-rw-r--r-- 1 root root  679 Nov  3 10:34 apps.json
drwxr-xr-x 2 root root 4096 Nov  3 10:34 credentials
-rw-r--r-- 1 root root   87 Nov  3 10:35 sunshine.conf
-rw-r--r-- 1 root root  258 Nov  3 10:37 sunshine.log
-rw-r--r-- 1 root root  831 Nov  3 10:37 sunshine.log.1
-rw-r--r-- 1 root root  116 Nov  3 10:37 sunshine.log.2
-rw-r--r-- 1 root root  831 Nov  3 10:37 sunshine.log.3
-rw-r--r-- 1 root root 4922 Nov  3 10:37 sunshine.log.4
-rw-r--r-- 1 root root 4792 Nov  3 10:36 sunshine.log.5
-rw-r--r-- 1 root root  144 Nov  3 10:35 sunshine_state.json
文件作用写入方
sunshine.conf主配置,key = value 键值行手工编辑,或 Web UI/API 保存
apps.json流应用清单(JSON)手工编辑,或 Web UI/API 保存
sunshine_state.json管理员凭据(用户名+salt+SHA256)与已配对客户端程序自动
credentials/CA 与服务端自签证书(Web UI 与 GameStream TLS 共用)程序自动
sunshine.log(.1~.5)运行日志,启动时轮转保留 5 份程序自动

sunshine.conf 初始为空文件——所有参数都走内置默认值;只有被显式保存过的键才会落盘。实测一个配置了 4 键的文件:

$ cat /root/.config/sunshine/sunshine.conf
min_log_level = debug
sunshine_name = game-lab
upnp = false
origin_web_ui_allowed = pc

2.2 sunshine.conf 语法(源码解析器语义)

解析逻辑在 src/config.cpp 的 parse_option() / parse_config()(920–1064 行),规则如下:

规则说明
基本格式key = value,等号两侧空白任意;键名区分大小写
注释# 起注释到行尾,行内注释也生效(值中 # 之后被剥掉)
空行 / 无等号行直接跳过,不报错
空值key = 后面无内容视为无效条目,跳过
列表值值以 [ 开头时按括号配对跨行读取(dd_mode_remapping、global_prep_cmd 等 JSON 列表可写多行);缺 ] 会告警 config: Missing ']' in config option: <key>
重复键try_emplace 语义——第一行生效,后续同名行被忽略
未知键不致命,启动时在标准输出打印 Warning: Unrecognized configurable option [<key>](见 7.1)

2.3 参数规模:99 个键 + 隐藏键 flags

从源码解析块(apply_config(),1626–1976 行)可提取 99 个配置键,即 Web UI Configuration 页与 sunshine.conf 的全部可写项。另有一个不在 99 键之列的隐藏键 flags:值为字符集合,0(PIN 走标准输入)、1(重置状态文件)、2(强制替换视频头)、p(开 UPnP),逐字符翻转对应开关(apply_flags(),1561–1586 行)。CLI 的 -0/-1/-2/-p 参数走的就是同一函数(见 D1)。

99 键按功能分组:网络监听 11、安全访问 6、标识/语言/日志 6、视频通用 10、编码器专属 38、软编 2、显示设备 10、音频 5、输入 18、系统集成 3(file_apps/global_prep_cmd/keybindings)。第 4 节逐组详解。

2.4 配置来源与优先级

一次启动的配置由三层叠加,后者不覆盖前者中已出现的键,实际优先级:

CLI key=value 参数  >  位置参数指定的 conf 文件  >  默认 ~/.config/sunshine/sunshine.conf  >  内置默认值

实测:默认配置文件里写 sunshine_name = game-lab,再以 CLI 覆盖启动:

$ sunshine sunshine_name=cli-override origin_web_ui_allowed=pc upnp=false

启动日志逐行回显最终生效值(log_config_settings(),1609–1619 行):

[2026-11-03 02:36:54.200]: Info: config: 'min_log_level' = debug
[2026-11-03 02:36:54.200]: Info: config: 'origin_web_ui_allowed' = pc
[2026-11-03 02:36:54.200]: Info: config: 'sunshine_name' = cli-override
[2026-11-03 02:36:54.200]: Info: config: 'upnp' = false

sunshine_name 取 CLI 值而非文件值——CLI 胜出。日志里出现的每一条 config: 'key' = value 就是"配置文件+CLI 叠加后的显式覆盖集",未出现的键静默用默认值。

3. 四种修改方式(全部实测)

3.1 方式一:直接编辑 sunshine.conf + 重启

最朴素也最可控:加一行 key = value,重启进程生效。deb 安装的用户级服务用 systemctl --user restart sunshine;本实测环境直接重启了服务进程。

实测写入两个键并重启后,API 查询与 GameStream 握手都能看到效果:

$ curl -sk -u admin:q1w2e3r4 https://localhost:47990/api/config
{"min_log_level":"debug","platform":"linux","status":true,"sunshine_name":"game-lab","version":"2026.914.233613"}

$ curl -s "http://localhost:47989/serverinfo?uniqueid=00000000-0000-4000-8000-000000000000&hostaddress=10.89.62.1&protocol=128&appversion=7.1.431" | grep -o "<hostname>[^<]*</hostname>"
<hostname>game-lab</hostname>

启动日志同时留下加载行(见 2.4)。适合版本化管理:把 sunshine.conf 纳入 Git/Ansible,重启即部署。

3.2 方式二:Web UI Configuration 页

浏览器打开 https://localhost:47990(Basic 认证后进主页),Configuration 页按分组列出全部可配项,每项显示当前值与默认值,改完点 Save。

抓包该页前端脚本(config_page 资源)确认它只调两个后端接口:保存 = POST /api/config(把页面上全部表单值整体提交),重启 = POST /api/restart。所以 Web UI 方式本质是 3.3 的批量封装;注意 Save 会重写整个 sunshine.conf(见 3.3 的覆盖语义——UI 提交的是全量表单,正常手编文件的人反而要当心 API 的部分提交)。

3.3 方式三:REST API GET/POST /api/config

POST /api/config 接收 JSON 对象,把每个非空键值写成 key = value 行。实测:

$ curl -sk -u admin:q1w2e3r4 -X POST -H 'Content-Type: application/json' \
    -d '{"sunshine_name":"game-lab","min_log_level":"debug"}' \
    https://localhost:47990/api/config
{"status":true}

$ cat /root/.config/sunshine/sunshine.conf
min_log_level = debug
sunshine_name = game-lab

两个必须知道的语义(源码 saveConfig(),confighttp.cpp 1529–1567 行):

  1. 整体覆盖,不是增量合并:write_file 用请求体里的键重写整个文件。实测覆盖前文件有 3 行(min_log_level/sunshine_name/foo_bar),只提交 {"sunshine_name":"game-lab"} 一次后:
$ cat /root/.config/sunshine/sunshine.conf
sunshine_name = game-lab

其余键全部丢失。Web UI 之所以安全,是因为它每次提交全量表单;用 curl/脚本做 API 自动化时必须先 GET 再合并再 POST,或改用文件编辑。

  1. null 与空串被跳过:值为 null 或空字符串的键不会写入文件(1552 行),即 API 无法把键"置空",只能删除整行(回到默认值需手工编辑或重写全量)。

GET /api/config 返回的是本次启动加载到的显式覆盖集(不含未覆盖的默认值),加 platform/version/status 三个固定字段——空配置文件时它就只有这三样:

$ curl -sk -u admin:q1w2e3r4 https://localhost:47990/api/config
{"platform":"linux","status":true,"version":"2026.914.233613"}

3.4 方式四:CLI——位置参数配置文件 + key=value 覆盖

启动命令支持两类位置参数(实测):

$ sunshine /tmp/my.conf                     # 第一个非 key=value 参数 = 配置文件路径
$ sunshine sunshine_name=cli-override       # key=value 直接覆盖单个键
$ sunshine port=48999                       # 见 4.1 端口平移实测

实测用 sunshine /tmp/my.conf 加载了非默认路径的文件,日志回显 config: 'upnp' = false、config: 'sunshine_name' = custom-path;key=value 形态见 2.4 的优先级实测。适合"一份服务器多套 Sunshine 配置"或 CI 冒烟测试。

4. 参数分组详解

以下表格的"默认值"列核对自源码默认块(config.cpp 737–913 行),"范围/取值"核对自解析块;标注【实测】的条目附本文回显证据。未标注者为源码/官方文档口径,在无 GPU 形态下无法端到端验证串流效果。

4.1 网络与监听(11 键)

键默认值取值/范围说明
port479891025–65514【实测】GameStream HTTP 基准端口,其余端口全部由它派生(见下方端口表)
address_familyipv4ipv4 / bothboth 同时监听 v6
bind_address空(全部接口)IP 字符串限定监听地址
upnpfalse布尔路由器自动映射;网络无 UPnP 设备时启动告警【实测】:Warning: Couldn't discover any IPv4 UPNP devices
external_ip空IP/域名对外通告地址(NAT 后手动指定)
ping_timeout10000(10s)-1~INT_MAX,毫秒客户端失联判定
packetsize0(自动)0~协议上限UDP 分片大小,MTU 异常网络才需要调
fec_percentage200–100前向纠错冗余比例,丢包高的网络加大
lan_encryption_mode00–2局域网加密:0=never / 1=opportunistic / 2=强制(源码常量 ENCRYPTION_MODE_NEVER 为默认)
wan_encryption_mode10–2公网加密,默认 opportunistic
min_threads2整数编码线程下限

port 一改,整族端口平移。实测 sunshine port=48999 起临时实例:

[2026-11-03 03:01:08.144]: Info: Configuration UI available at [https://localhost:49000]
$ curl -s "http://localhost:48999/serverinfo?uniqueid=...&protocol=128&appversion=7.1.431"
... state="SUNSHINE_SERVER_FREE" ...
<HttpsPort>48994</HttpsPort>
<ExternalPort>48999</ExternalPort>

端口派生公式(源码常量 + 本文实测汇总):

端口公式默认值48999 实例实测
GameStream HTTPport4798948999
GameStream HTTPSport - 54798448994
Web UI / APIport + 14799049000
RTSPport + 214801049020
UDP 控制/视频/音频port + 9/10/1147998/47999/4800049008–49010

同机第二个实例必须错开 port,否则 RTSP 先撞:Fatal: Couldn't bind RTSP server to port [48010], Address already in use(实测第二实例 fatal 原文)。

4.2 安全与访问控制(6 键)

键默认值取值/范围说明
origin_web_ui_allowedlan仅 pc / lan / wan(源码白名单)Web UI/API 访问面:pc 仅本机回环,lan 加内网段,wan 放开全部
certcredentials/ 下默认证书路径TLS 服务端证书
pkeycredentials/ 下默认私钥路径TLS 私钥
file_statesunshine_state.json文件名凭据与配对状态存储
credentials_file同 file_state路径可单独指定凭据文件(解析顺序上先取 file_state 再被本键覆盖,1763–1767 行)
csrf_allowed_origins空(内置 localhost 集始终生效)https:// 起源列表追加允许携带 CSRF 令牌的浏览器起源

origin_web_ui_allowed 三档实测对照(设 pc 后):

来源请求结果
本机 127.0.0.1GET https://localhost:47990/200
局域网地址 10.89.62.1同上403

取值写错(如 all)会被源码白名单拒绝并回退默认——string_restricted_f 语义,配错不生效也不报错,排障时先看日志里 config: 行的实际值。

csrf_allowed_origins 机制(源码 confighttp.cpp 849–906 行 + 实测):内置默认永远包含 https://localhost、https://127.0.0.1、https://[::1] 及各自带 UI 端口(port+1)的变体;用户追加的条目必须以 https:// 开头且长度 > 8,否则日志告警 Invalid 'csrf_allowed_origins' entry rejected: <origin>。校验逻辑:带 Origin/Referer 头且命中允许列表 → 放行;两个头都不带(curl/脚本)→ 直接放行;带了但不命中 → 要求 X-CSRF-Token 头或 ?csrf_token= 查询参数。实测:

$ curl -sk -u admin:q1w2e3r4 -X POST -H 'Content-Type: application/json' \
    -H 'Origin: https://evil.example' -d '{"sunshine_name":"hijack"}' \
    https://localhost:47990/api/config
{"error":"Missing CSRF token","status":false,"status_code":400}

[2026-11-03 03:01:48.301]: Error: Web UI: [10.89.62.1] -- CSRF protection blocked request from origin: https://evil.example
$ TOKEN=$(curl -sk -u admin:q1w2e3r4 https://localhost:47990/api/csrf-token | jq -r .csrf_token)   # 32 字符
$ curl -sk -u admin:q1w2e3r4 -X POST -H 'Content-Type: application/json' \
    -H 'Origin: https://evil.example' -H "X-CSRF-Token: $TOKEN" \
    -d '{"sunshine_name":"game-lab"}' https://localhost:47990/api/config
{"status":true}

令牌由 GET /api/csrf-token 签发、按客户端(Basic 用户名,否则来源 IP)一次性存储。用反向代理把 UI 暴露到自定义域名时,就要把该域名加进 csrf_allowed_origins,否则浏览器里的 Save 全部 400。

4.3 标识、语言与日志(6 键)

键默认值取值/范围说明
sunshine_name主机名字符串显示名;进 serverinfo XML 的 <hostname>【实测 game-lab 生效】,也是 Moonlight 列表里看到的名字
localeen源码白名单 22 种(含 zh、zh_TW)UI 语言;GET /api/configLocale 回显【实测】:{"locale":"en","status":true}
min_log_level2(Info)字符串 verbose/debug/info/warning/error/fatal/none 或单个数字 0–6见 7.2 的数字坑
log_path~/.config/sunshine/sunshine.log路径日志文件位置
system_traytrue布尔托盘;无头环境自动失败(Error: System tray is not initialized,实测)
notify_pre_releasesfalse布尔是否提示预发布更新

4.4 视频编码——通用(10 键)

键默认值取值/范围说明
qp28整数量化参数,越小画质越高码率越大
hevc_mode00–3H.265 对客户端的暴露档位(0=关,3=强制开,1/2 按硬件/软件支持探测)
av1_mode00–3AV1 同构档位
max_bitrate0(不限)整数码率上限
minimum_fps_target0(=跟随帧率)0.0–1000.0最低帧率目标(浮点)
encoder空(自动探测)nvenc/vaapi/vulkan/software 等强制编码后端;启动探测顺序实测为 nvenc→vulkan→vaapi→software
capture空(自动)平台捕获名强制画面捕获方式
adapter_name空字符串多显卡时指定编码适配器(Windows 向)
output_name空字符串多显示器时指定输出名
fp16_sdr_gamma_encodedfalse布尔FP16 SDR 伽马编码

hevc_mode/av1_mode 是"客户端能力协商"开关:Moonlight 端请求 H.265/AV1 时,主机按此档位与自身编码器能力决定是否接受;0 表示只走 H.264。

4.5 视频编码——硬件编码器专属组(38 键)

六组前缀按键名归属:nvenc_*(NVIDIA)、vaapi_*(Linux VAAPI,Intel/AMD)、qsv_*(Intel Quick Sync)、amd_*(AMD AMF,Windows)、vt_*(Apple VideoToolbox,macOS)、vk_*(Vulkan)。跨平台写了不匹配前缀的键不会报错,只是不生效。

组键(默认值)要点
nvenc(8)nvenc_preset(1–7 范围校验)、nvenc_vbv_increase(0–400)、nvenc_spatial_aq(bool)、nvenc_twopass(枚举:关闭/1/4 分辨率/全分辨率二次编码)、nvenc_h264_cavlc(bool)、nvenc_split_encode(枚举)、nvenc_realtime_hags(true)、nvenc_opengl_vulkan_on_dxgi(true)、nvenc_latency_over_power(true)后三个布尔是行为开关:HAGS 实时调度、GL/Vk 走 DXGI、性能模式优先于省电
vaapi(4)vaapi_quality(auto)、vaapi_rc(auto,字符串枚举)、vaapi_blbrc(false)、vaapi_strict_rc_buffer(false)Linux 无独显/Intel 核显主路;vaapi_rc 同时接受枚举名与字符串
qsv(3)qsv_preset(medium)、qsv_coder(auto)、qsv_slow_hevc(false)Intel;qsv_slow_hevc 换质量耗时
amd(8)amd_usage(ultralowlatency)、amd_rc(vbr_latency)、amd_quality(balanced)、amd_preanalysis(false)、amd_vbaq(true)、amd_enforce_hrd(false)、amd_max_au_size(不限)、amd_coder(auto)AMF;usage/rc/quality 三个键各自映射到 H.264/H.265/AV1 三套枚举(源码 1663–1697 行)
vt(4)vt_coder、vt_software、vt_realtimemacOS VideoToolbox(本专栏 Linux 形态不实测)
vk(3)vk_tune(2=low-latency)、vk_rc_mode(2=CBR)、vk_quality(2,接受字符串 speed/balanced/quality→1/2/3)Vulkan 编码后端,启动探测里排第二

4.6 软件编码(2 键)

键默认值说明
sw_presetsuperfastx264/x265 preset 风格字符串;同时映射 SVT-AV1 preset(源码 svtav1_preset_from_view)
sw_tunezerolatencyx264 tune;zerolatency 关闭 lookahead,牺牲压缩率换延迟

软件编码只在四类硬件后端全探测失败时兜底,高帧率高分辨率不实用(选型见 Sunshine 介绍与选型指南)。

4.7 显示设备 dd_*(10 键,Windows Display-Device 集成)

dd_ 前缀键对接 Windows 侧显示配置(DisplayDevice 库),Linux 形态基本用不到,列出供对照:

键默认值说明
dd_configuration_optiondisabled是否允许会话中改显示配置
dd_resolution_option / dd_refresh_rate_optionautomatic分辨率/刷新率策略(automatic/manual)
dd_manual_resolution / dd_manual_refresh_rate空手动值
dd_hdr_optionautomaticHDR 开关策略
dd_config_revert_delay3000(ms,范围 0–INT_MAX)会话结束后延迟还原
dd_config_revert_on_disconnect空断连时还原策略
dd_mode_remapping空JSON 列表,可跨行:mixed/resolution_only/refresh_rate_only 三类规则,每条含 requested_resolution/requested_fps/final_resolution/final_refresh_rate(源码 706–731 行)
dd_wa_hdr_toggle_delay0(范围 0–3000)HDR 切换兼容 workaround 延时

4.8 音频(5 键)

键默认值说明
stream_audiotrue总开关
install_steam_audio_driverstrue自动装 Steam 虚拟声卡驱动(Windows)
external_audiofalse主机继续本地出声
audio_sink空(默认输出设备)抓取哪个输出设备
virtual_sink空虚拟输出设备名

Linux 侧音频抓取依赖 PulseAudio/PipeWire 环境;无头服务器上这组键可配但无实际抓取对象。

4.9 输入设备(18 键)

键默认值取值说明
keyboard / mouse / controllertrue / true / true布尔三类输入注入总开关
always_send_scancodestrue布尔始终用扫描码传键
high_resolution_scrollingtrue布尔高精度滚轮
native_pen_touchtrue布尔原生笔/触控事件
gamepadauto平台支持集虚拟手柄模拟类型;实测 Linux 支持 auto/generic/x360/xone/xseries/ds4/ds5/switch
gamepad_driver平台默认all/virtualhid/vigembus/none(源码白名单)手柄驱动后端(ViGEmBus 为 Windows 专属)
ds4_back_as_touchpad_clicktrue布尔DS4 返回键映射为触摸板点击
motion_as_ds4 / touchpad_as_ds4true / true布尔客户端手柄体感/触摸板按 DS4 语义重放
virtualhid_randomize_mactrue布尔虚拟 HID 随机 MAC
key_rightalt_to_key_winfalse布尔右 Alt 映射 Win 键(实现即往 keybindings 加 0xA5→0x5B,源码 1834–1839 行)
back_button_timeout-1(禁用)毫秒长按返回键触发组合
key_repeat_delay500毫秒按键连发起始延迟
key_repeat_frequency≈24.9>0连发频率(源码默认周期 1/24.9 秒)

无头/无 uinput 权限环境实测整排告警:Warning: Gamepad auto is disabled due to gamepads.virtualhid-not-available……Warning: No gamepad input is available——手柄链路要主机侧内核模块/权限就绪才可用。

4.10 系统集成(3 键)

键默认值说明
file_appsapps.json应用清单路径;文件不存在时自动从安装资产复制默认模板(源码 1816–1826 行)
global_prep_cmd空全局前置命令列表([{"do":...,"undo":...}],可跨行);每个应用启动前执行,除非应用设 exclude-global-prep-cmd
keybindings内置 3 对映射键码重映射表(0x10→0xA0、0x11→0xA2、0x12→0xA4,即 F13–F15 组合键),UI 与 key_rightalt_to_key_win 都写这张表

5. apps.json 应用清单详解

5.1 文件结构与字段表

顶层两个键:env(全局环境变量,支持 $(PATH) 式引用)与 apps(应用数组)。单个应用字段(源码 API 注释 + 实测文件,confighttp.cpp 1137–1165 行):

字段类型说明
name字符串显示名,保存时数组按它排序【实测】
cmd字符串前台命令(进程退出=会话结束)
detached字符串数组后台命令(不等它退出)
prep-cmd对象数组每项 {do, undo, elevated?}:启动前执行 do,结束后执行 undo
env对象应用级环境变量
image-path字符串封面 PNG 路径(相对或绝对;desktop.png 为内置)
output字符串应用日志输出路径
exit-timeout整数结束等待秒数(历史版本写成字符串会被自动转换,源码 1104–1119 行)
exclude-global-prep-cmd布尔跳过 global_prep_cmd
elevated / auto-detach / wait-all布尔提权运行 / 自动转后台 / 等待全部子进程

5.2 默认模板(实测)

全新环境首启自动生成,实测内容:

{
  "env": { "PATH": "$(PATH):$(HOME)/.local/bin" },
  "apps": [
    { "name": "Desktop", "image-path": "desktop.png" },
    { "name": "Low Res Desktop", "image-path": "desktop.png",
      "prep-cmd": [{ "do": "xrandr --output HDMI-1 --mode 1920x1080",
                     "undo": "xrandr --output HDMI-1 --mode 1920x1200" }] },
    { "name": "Steam Big Picture",
      "detached": ["setsid steam steam://open/bigpicture"],
      "prep-cmd": [{ "do": "", "undo": "setsid steam steam://close/bigpicture" }],
      "image-path": "steam.png" }
  ]
}

三个模板覆盖"串桌面 / 串低分辨率桌面 / 串 Steam 大屏",也是字段用法的现成示例。

5.3 API 管理(实测 CRUD)

与 /api/config 不同,应用清单改动即时生效、无需重启(保存后源码调用 proc::refresh(),1227 行)。完整一轮实测:

$ curl -sk -u admin:q1w2e3r4 -X POST -H 'Content-Type: application/json' \
    -d '{"name":"Test App","cmd":"echo hello","index":-1}' \
    https://localhost:47990/api/apps
{"status":true}

$ curl -sk -u admin:q1w2e3r4 https://localhost:47990/api/apps | jq '[.apps[].name]'
["Desktop", "Low Res Desktop", "Steam Big Picture", "Test App"]

$ curl -sk -u admin:q1w2e3r4 -X DELETE https://localhost:47990/api/apps/3
{"result":"application 3 deleted","status":true}

要点:index: -1 新增、index: N 替换第 N 项;缺 index 直接 400——{"error":"[json.exception.type_error.302] type must be number, but is null","status":false,"status_code":400}(实测原文);保存后文件被重写为 4 空格缩进且对象内键名按字典序(实测 tail 可见 "cmd" 排在 "name" 前)。

5.4 文件缺失自动生成

file_apps 指向的文件不存在时,启动即从安装目录资产复制默认 apps.json 并补 owner 读写权限(源码 1816–1826 行)。隔离配置目录首启实测目录树:

/tmp/iso/.config/sunshine/
├── apps.json          # 自动复制的默认模板
├── credentials/       # 自动生成的 CA 与自签证书
├── sunshine.conf      # 空文件
├── sunshine.log
└── sunshine_state.json # 首启设凭据后出现

6. 生效机制与日志

6.1 哪些立即生效、哪些要重启

改动生效方式
apps.json(含 API/Web UI 应用页)立即(proc::refresh)
sunshine.conf 任何键重启进程(配置只在启动时解析一次,apply_config 无热加载路径)
sunshine_name重启后 serverinfo 可见【实测】
port重启,全家端口平移【实测】

6.2 重启路径与无头坑

Web UI 的重启按钮调 POST /api/restart。有头环境优雅重启;无头环境主循环已自行退出(Main loop has exited,进程靠 UI 线程存活),此时触发 restart 会卡:

[2026-11-03 02:37:52.104]: Info: Interrupt handler called
[2026-11-03 02:38:02.105]: Fatal: 10 seconds passed, yet Sunshine's still running: Forcing shutdown

之后进程不退出,需要外部手段拉起(服务管理器 restart)。实测环境用的就是服务级重启替代。

6.3 配置加载日志与轮转

每次启动把显式覆盖集逐行打为 config: 'key' = value(2.4 实测),敏感项在名单内的打 [redacted]。日志轮转:每次启动 sunshine.log → .1,依次后移,保留 5 份(2.1 实测目录里 .1~.5 齐全)。排障时"上一次启动为什么挂了"直接翻对应序号文件。

7. 常见问题

7.1 未知键:只在控制台告警,不进日志文件

sunshine.conf 写错键名不致命:服务照常起(实测 code=200),但 sunshine.log 查不到任何痕迹——警告走 std::cout(源码 1971–1974 行,且要求 min_log_level ≤ 3),只出现在进程标准输出:

Warning: Unrecognized configurable option [foo_bar]

systemd/journal 形态能在服务日志里看到,纯 sunshine.log 里没有。排查"配置不生效"先确认键名在 99 键之列,再看控制台输出。

7.2 min_log_level:字符串或单个数字

解析分支(源码 1937–1962 行):先匹配 7 个字符串(verbose=0 … none=6);否则只看值的首字符是否为 0–6。所以 min_log_level = 1 生效(debug),min_log_level = 10 会被截成 1,min_log_level = 7 及乱写值静默保持默认 2。建议一律写字符串。

7.3 port 改动 = 全家端口平移

防火墙/映射只放行 47989–47990 的脚本在改 port 后全部失效,按 4.1 引用表重算:HTTPS=port-5、UI=port+1、RTSP=port+21、UDP=port+9/10/11。

7.4 API 保存配置丢键

POST /api/config 是整体覆盖(3.3 实测 3 行变 1 行)。脚本化正确姿势:GET /api/config 取回当前覆盖集 → 合并新键 → 全量 POST。

7.5 白名单键配错静默回退

origin_web_ui_allowed、address_family、gamepad_driver、locale 等走源码白名单校验,非法值不报错、维持默认。确认办法:看启动 config: 行 + 行为验证(如 UI 是否 403)。

8. 参考链接