Solara 2.1.6 配置参数详解

Solara 2.1.6 配置参数逐项详解:PASSWORD/API_BASE_URL/language 三个可用环境变量的生效与失效边界、PORT/HOST/DATA_DIR 被入口脚本固定的实测结论、五类缓存参数、前端内置常量,附三种场景的生产配置模板与故障注入回显。

SolaraSolara音乐流媒体自托管2026/10/25

Solara 2.1.6 配置参数详解

目标:把 Solara 2.1.6 的全部可配置项讲透——哪些环境变量真正生效、哪些被入口脚本固定、缓存与前端常量如何影响行为,并给出可照抄的生产配置模板。文中每个「生效/不生效」结论都配有 2026-10-25 在 Ubuntu 22.04 + Docker Engine 29.9.2 上的实测回显。

一、环境信息

  • 操作系统:Ubuntu 22.04.5 LTS(x86_64)
  • Docker Engine:29.9.2,Docker Compose 插件 v5.6.1
  • Solara 版本:2.1.6(镜像 ghcr.io/akudamatata/solara:2.1.6,linux/amd64,765020763 字节)
  • 部署形态:Docker Compose 单实例,宿主端口 8136 → 容器 8787
  • 实测基线:空载 CPU 0.88% / 内存 195.4 MiB / 71 PIDs
  • 演示访问口令:q1w2e3r4
  • 配置形态:无外部配置文件,全部配置项来自环境变量;容器内 /app/.dev.vars 由入口脚本在每次启动时生成

二、前置条件

  1. 已按部署篇完成一次可运行的部署,容器状态为 Up。验证:
docker compose ps

实测输出:

NAME                IMAGE                              COMMAND                  SERVICE   CREATED          STATUS          PORTS
solara-v2.1.6-app   ghcr.io/akudamatata/solara:2.1.6   "/app/docker-entrypo…"   solara    14 minutes ago   Up 14 minutes   0.0.0.0:8136->8787/tcp, [::]:8136->8787/tcp
  1. 已取得有效登录 Cookie(后续受保护接口都要带它):
cd /opt/solara
curl -s -c cookies.txt -X POST http://127.0.0.1:8136/api/login \
  -H 'Content-Type: application/json' -d '{"password":"q1w2e3r4"}'

实测输出:

{"success":true}

三、参数查看与修改方式

Solara 的参数分三层,查看与修改方式各不相同:

层内容查看方式修改方式
编排层(用户可改)PASSWORD、API_BASE_URL、languagedocker inspect 看容器实际环境变量改 docker-compose.yml 的 environment → docker compose up -d --force-recreate
入口脚本层(固定)PORT=8787、HOST=0.0.0.0、DATA_DIR=/data、WRANGLER_API_URL=http://127.0.0.1:8788docker exec 读启动日志改不动,需改宿主端口映射或重建镜像
源码内置层(前端常量)音源列表、音质档位、雷达榜单、缓存 TTL浏览器调试控制台 / 源码需改源码重新构建镜像

3.1 查看容器实际生效的环境变量

docker inspect solara-v2.1.6-app \
  --format '{{range .Config.Env}}{{println .}}{{end}}' | grep -E 'PASSWORD|API_BASE_URL|language'

实测输出:

PASSWORD=q1w2e3r4
API_BASE_URL=https://music-api.gdstudio.xyz/api.php
language=ENG

💡 说明:这里看到的是编排层注入的原始值。Wrangler 进程读取的是入口脚本生成的 /app/.dev.vars,内容与这三个变量一一对应(见 5.3 节回显)。

3.2 修改参数的正确姿势

# 1. 编辑编排文件(示例:改口令)
vi /opt/solara/docker-compose.yml

# 2. 重建容器让新环境变量生效
cd /opt/solara && docker compose up -d --force-recreate

实测输出:

 Container solara-v2.1.6-app Creating 
 Container solara-v2.1.6-app Created 
 Container solara-v2.1.6-app Starting 
 Container solara-v2.1.6-app Started 

⚠️ 注意:docker compose restart 不会重读编排文件。restart 只是重启同一份容器配置,环境变量仍是旧值。验证方式:改口令后仅执行 restart,用新口令登录仍返回 {"success":false};执行 up -d --force-recreate 后新口令才生效。

四、参数总览

4.1 环境变量

变量默认值生效性作用面详见
PASSWORD空(开放访问)✅ 生效Express 认证中间件 + Wrangler 中间件5.1
API_BASE_URLhttps://music-api.gdstudio.xyz/api.php✅ 生效/proxy 上游地址5.2
language(或 LANGUAGE)空(中文)⚠️ Docker 形态仅写入不生效Wrangler HTML 注入5.3
PORT8787❌ 被入口脚本覆盖Express 监听端口5.4
HOST0.0.0.0❌ 被入口脚本覆盖Express 监听地址5.4
DATA_DIR/data❌ 被入口脚本覆盖SQLite 数据目录5.5
WRANGLER_API_URLhttp://127.0.0.1:8788入口脚本注入Express→Wrangler 内部转发5.4

关键解读:只有 3 个变量是用户真正能调的。PORT/HOST/DATA_DIR 虽在 Express 源码里支持读取,但 docker-entrypoint.sh 在启动 Node 前无条件 export 了固定值,所以环境变量被覆盖。

4.2 服务端内置常量

常量取值位置说明
容器内监听端口8787docker-entrypoint.sh固定 export PORT=8787
Wrangler 监听127.0.0.1:8788docker-entrypoint.sh仅回环,不对外暴露
数据库路径/data/solara.dbserver/db.jsSQLite 主库,WAL 模式
认证 Cookie 名authserver/routes/login.js值 = Base64(口令)
Cookie 有效期48 小时(Max-Age=172800)server/routes/login.js实测响应头一致
/proxy 缓存 TTL300 秒server/routes/proxy.js空结果/错误不缓存
/palette 缓存 TTL3600 秒server/routes/palette.js封面取色结果
上传体上限10 MBserver/index.jsexpress.json({limit:'10mb'})
酷我音频代理白名单*.kuwo.cnserver/routes/proxy.js仅该域可作流代理目标

4.3 前端内置常量(源码级)

常量取值影响
SOURCE_OPTIONSnetease / kuwo / joox / bilibili搜索源下拉的四项,实测仅 netease 可用
QUALITY_OPTIONS128 / 192 / 320 / 999音质档位,默认 320
RADAR_PLAYLISTS热歌榜 3778678、飙升榜 19723756、新歌榜 3779629、潮流风向榜 13372522766、原创榜 2884035、全球说唱榜 14028249541、Billboard 60198音乐雷达可勾选榜单
STORAGE_KEYS_TO_SYNC15 个键(playlistSongs、favoriteSongs、playbackQuality…)哪些前端状态同步到服务端
PALETTE_STORAGE_KEYpaletteCache.v3前端封面取色缓存键

五、核心参数分类详解

5.1 访问控制:PASSWORD

作用:全站单一共享口令。未配置时完全开放;配置后所有非公开路径都要校验 Cookie auth。

生效行为(三种场景均已实测):

场景 A:配置口令(本文基线配置)

curl -s -o /dev/null -w "GET / -> %{http_code} redirect=%{redirect_url}\n" http://127.0.0.1:8136/
curl -s -X POST http://127.0.0.1:8136/api/login -H 'Content-Type: application/json' \
  -d '{"password":"wrong-pass-2026"}' -w "\nHTTP %{http_code}\n"

实测输出:

GET / -> 302 redirect=http://127.0.0.1:8136/login
{"success":false}
HTTP 401

正确口令则下发 Cookie:

Set-Cookie: auth=cTF3MmUzcjQ=; Max-Age=172800; Path=/; SameSite=Lax; HttpOnly

场景 B:口令留空(删除 PASSWORD 行后重建;本文为不打断基线服务,用独立实例 solara-nopw、宿主端口 8142 验证)

docker logs solara-nopw | grep Password
curl -s -o /dev/null -w "GET / -> %{http_code}\n" http://127.0.0.1:8142/

实测输出:

  Password  : not set (open access)
  Password: ✗ not set (open access)
GET / -> 200

⚠️ 注意:留空即任何能访问端口的人都能直接进入播放器,且 /api/storage 也开始接受匿名读写。仅在完全可信的内网、或前面已有反向代理做统一认证时才可留空。

场景 C:口令变更。改 PASSWORD 并重建容器后,旧 Cookie 立即失效(auth 是口令的 Base64,口令变了值就变了),所有已登录用户需重新登录。

💡 技术拓展:访问控制模型不止「共享口令」一种——

模型代表实现特点适用规模
共享口令Solara(本文)零账号体系,一个口令全站通行个人/家庭 1-3 人
多用户账号Navidrome、Jellyfin每人独立账号、权限、歌单团队/多人
前置统一认证Nginx basic auth、Authelia、OAuth2 Proxy认证交给反代,应用本身不设防已有 SSO 的组织

Solara 自身只提供第一种;需要多用户时只能靠反向代理层叠加第三种。

安全提示(实测缺陷):口令会以明文形式出现在容器启动日志(Password : configuredq1w2e3r4),因为入口脚本把 configured 与口令值做了字符串拼接。处理建议见《Solara 日常运维手册》。

5.2 上游音源:API_BASE_URL

作用:决定搜索、歌词、封面、播放直链全部转发到哪个聚合 API。Express 的 /proxy 会把它转发给内部 Wrangler,由 Wrangler 用 BoringSSL 指纹请求上游。

默认值实测:

curl -s -b cookies.txt --max-time 60 \
  "http://127.0.0.1:8136/proxy?types=search&source=netease&name=%E5%91%A8%E6%9D%B0%E4%BC%A6&count=5&pages=1" \
  -o /dev/null -w "HTTP %{http_code} bytes=%{size_download} time=%{time_total}s\n"

实测输出:

HTTP 200 bytes=1188 time=6.454419s

故障注入实测:把 API_BASE_URL 指向不可达地址(http://127.0.0.1:9/api.php)后重建容器,搜索接口返回:

HTTP 500

响应体为 Wrangler 侧抛出的上游连接错误(节选):

Error: Network connection lost.
    at async proxyApiRequest (file:///app/functions/proxy.ts:130:20)
    at async i18nMiddleware (file:///app/functions/_middleware.ts:86:20)

⚠️ 注意:上游不可达时前端表现为「搜索无结果」并可能弹出错误提示,服务端日志是判定位数的第一手依据。恢复正确地址后重建容器即恢复 200。

音源支持现状(实测):API_BASE_URL 只是地址,能搜到哪些源由上游决定。实测 source=kuwo 返回:

{"detail":"Value of `source` is not supported."}
HTTP 400

README 宣称的「网易云/QQ/酷狗」在当前上游已收缩为仅 netease 可用。前端下拉仍显示四项,属于前端常量与上游能力的错位。

高码率参数(br)实测边界:

取值实测结果
128HTTP 200,2.9 秒解析成功(mp3,3820077 字节)
320115 秒超时(HTTP 000)
999(FLAC)115 秒超时(HTTP 000)

💡 说明:br 不是 Solara 的配置项,而是每次请求 /proxy?types=url&br= 时携带的参数,由前端音质偏好决定。生产默认建议 128/192。

5.3 界面语言:language / LANGUAGE

作用:Wrangler 中间件检测到 ENG 时,向 HTML <head> 注入 <script>window.SITE_LANGUAGE = "ENG";</script>,前端 i18n 模块据此切换文案。

实测结论:Docker 形态下写入成功、界面不切换。 证据链三步:

第一步,变量确实写进了 Wrangler 读取的文件:

docker exec solara-v2.1.6-app cat /app/.dev.vars

实测输出:

PASSWORD="q1w2e3r4"
API_BASE_URL="https://music-api.gdstudio.xyz/api.php"
language="ENG"

第二步,经 8787(Express 对外端口)取回的页面里没有注入:

curl -s http://127.0.0.1:8136/login | grep -c 'SITE_LANGUAGE'

实测输出:

0

第三步,直连容器内 8788(Wrangler)取回的页面里有注入:

docker exec solara-v2.1.6-app node -e "fetch('http://127.0.0.1:8788/login').then(r=>r.text()).then(t=>console.log('count='+(t.match(/SITE_LANGUAGE/g)||[]).length))"

实测输出:

count=1

原因定位(源码核实):Docker 形态下 GET / 与 GET /login 由 Express 的 res.sendFile() 直接返回静态文件,请求根本不经过 Wrangler 的 i18nMiddleware;只有 /proxy 会转发给 Wrangler。因此注入逻辑在 Docker 形态下永远碰不到页面。

⚠️ 注意:官方 README 称 Docker 下 language=ENG 可切英文,与实测不符。该参数仅在 Cloudflare Pages 部署形态生效。Docker 部署可保留该行(无副作用),但不要指望界面变化。

5.4 端口与监听:PORT / HOST

实测结论:不可通过环境变量修改。

入口脚本在启动 Node 前硬编码:

export PORT=8787
export HOST=0.0.0.0
export DATA_DIR=/data
export WRANGLER_API_URL=http://127.0.0.1:8788

因为 export 发生在读取环境变量之后、node server/index.js 之前,编排文件里传的 PORT/HOST 会被覆盖。

正确改法:改宿主映射,容器内始终 8787:

    ports:
      - "9090:8787"   # 宿主 9090 → 容器 8787

WRANGLER_API_URL 同理固定为 http://127.0.0.1:8788:Wrangler 与 Express 在同一容器内,走回环最省事,也让 8788 天然不暴露到宿主机。

5.5 数据目录:DATA_DIR / volumes

实测结论:数据目录固定为 /data,只能通过 volumes 把宿主目录挂上去。

    volumes:
      - ./data:/data

目录内容与职责:

文件/目录作用是否必须备份
solara.dbSQLite 主库:playback_store(播放状态/队列)、favorites_store(收藏)✅ 必须
solara.db-walWAL 日志,含尚未 checkpoint 的最新写入✅ 必须(与主库一起)
solara.db-shmWAL 共享内存索引✅ 建议一起
v3/Wrangler/miniflare 持久化(cache/、d1/、workflows/)可选,可重建

存储接口验证(确认数据库真的在读写):

curl -s -b cookies.txt "http://127.0.0.1:8136/api/storage?status=1"
curl -s -b cookies.txt -X POST http://127.0.0.1:8136/api/storage \
  -H 'Content-Type: application/json' -d '{"data":{"doc-check-2026-10-25":"solara-2.1.6-ok"}}'
curl -s -b cookies.txt "http://127.0.0.1:8136/api/storage?keys=doc-check-2026-10-25"

实测输出:

{"d1Available":true}
{"d1Available":true,"updated":1}
{"d1Available":true,"data":{"doc-check-2026-10-25":"solara-2.1.6-ok"}}

💡 说明:d1Available 是历史命名(Solara 原先用 Cloudflare D1),Docker 形态下由本地 SQLite 顶替,返回值保持 true 表示存储可用。

5.6 缓存参数

Solara 有两级内存缓存,没有可配置的环境变量,但理解它们的行为对排障与容量规划很重要。

/proxy 响应缓存(TTL 300 秒):

  • 缓存键 = 去掉随机签名 s 与 nocache 后的完整 URL,因此重试同一请求会命中同一条缓存
  • 只有 HTTP 200 且响应体不含 "error" / "status":0 才写缓存
  • 搜索结果为空([])不写缓存——避免把一次「API 繁忙」的空结果钉住 5 分钟
  • 请求带 nocache=true 时强制回源且不回写

命中判定实测(连续两次相同请求,看响应头):

curl -s -b cookies.txt -D h1.txt -o /dev/null \
  "http://127.0.0.1:8136/proxy?types=search&source=netease&name=%E5%91%A8%E6%9D%B0%E4%BC%A6&count=5&pages=1"
curl -s -b cookies.txt -D h2.txt -o /dev/null \
  "http://127.0.0.1:8136/proxy?types=search&source=netease&name=%E5%91%A8%E6%9D%B0%E4%BC%A6&count=5&pages=1"
grep -i 'x-cache-status' h1.txt h2.txt

实测输出:

h1.txt:X-Cache-Status: MISS
h2.txt:X-Cache-Status: HIT

/palette 封面取色缓存(TTL 3600 秒):

curl -s -b cookies.txt --max-time 30 -o palette.json \
  -w "HTTP %{http_code} bytes=%{size_download} time=%{time_total}s\n" \
  "http://127.0.0.1:8136/palette?image=https%3A%2F%2Fp2.music.126.net%2FyD9vbpuILH-tqNRIaP640g%3D%3D%2F109951163038292176.jpg%3Fparam%3D300y300"

实测输出(第二次调用,命中缓存):

HTTP 200 bytes=601 time=0.002076s

返回结构(键清单 + 关键色值):

keys: ['source', 'baseColor', 'averageColor', 'accentColor', 'contrastColor', 'gradients', 'tokens']
baseColor: #ba7f5d accentColor: #ba7f5d

palette 返回的 baseColor/accentColor/gradients/tokens 就是前端「流体极光」背景的取色结果;命中缓存时 2 毫秒返回,未命中需下载并解码封面(数百毫秒级)。

💡 技术拓展:缓存淘汰策略不止本文所用的「固定 TTL」——

策略特点适用场景
TTL(本文)到期即失效,实现简单、内存可控搜索结果这类「短时有效」数据
LRU淘汰最久未使用,容量恒定命中分布均匀的大对象缓存
LFU淘汰访问频次最低有明显热点长尾的数据
FIFO先进先出,不看访问顺序写入的流式队列

Solara 选用 TTL 300s/3600s:搜索与直链本就有时效(直链 URL 带时间戳、过期即失效),用 TTL 比 LRU 更贴合语义;进程内 Map 每 5 分钟清理一次过期项,避免泄漏。

5.7 前端内置常量(改需重建镜像)

这些常量写死在前端源码里,运行期无法通过环境变量覆盖:

常量位置当前值改法
音源列表js/constants.js SOURCE_OPTIONSnetease / kuwo / joox / bilibili改源码 → 自建镜像
音质档位js/constants.js QUALITY_OPTIONS128 / 192 / 320 / 999,默认 320同上
雷达榜单js/constants.js RADAR_PLAYLISTS7 个网易云榜单 ID同上
默认雷达勾选js/constants.js DEFAULT_RADAR_GENRES热歌榜/新歌榜/飙升榜同上
同步键集合js/constants.js STORAGE_KEYS_TO_SYNC15 个前端状态键同上

💡 说明:音质、雷达榜单、搜索源三项在运行期可通过界面调整——双击桌面端左上角 Logo(移动端双击顶部标题栏)呼出隐藏设置面板;只有「增删选项本身」才需要改源码。

六、生产配置模板

6.1 模板 A:个人/家庭最小配置(1 核 1 GB、10 GB 磁盘、内网使用)

name: solara-v216-stack

services:
  solara:
    image: ghcr.io/akudamatata/solara:2.1.6
    container_name: solara-v2.1.6-app
    restart: unless-stopped
    init: true
    ports:
      - "8136:8787"
    environment:
      - PASSWORD=q1w2e3r4
      - API_BASE_URL=https://music-api.gdstudio.xyz/api.php
    volumes:
      - ./data:/data
    networks:
      solara-net:
        ipv4_address: 10.89.65.10

networks:
  solara-net:
    driver: bridge
    ipam:
      config:
        - subnet: 10.89.65.0/24

适用说明:内存 195 MiB 级、磁盘 < 1 MB 数据、镜像 765 MB;不加 language(Docker 下无效,省一行);口令必须设置(内网也建议设)。

6.2 模板 B:反向代理 + HTTPS(2 核 2 GB、20 GB SSD、公网暴露)

Nginx 侧(假设 Solara 在 8136,域名 music.example.com):

server {
    listen 443 ssl;
    server_name music.example.com;

    ssl_certificate     /etc/letsencrypt/live/music.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/music.example.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:8136;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;   # 让 Solara 下发 Secure Cookie
        proxy_read_timeout 300s;                    # 上游解析慢时留足时间
    }
}

配套编排(只监听回环,强制走反代):

    ports:
      - "127.0.0.1:8136:8787"
    environment:
      - PASSWORD=q1w2e3r4
      - API_BASE_URL=https://music-api.gdstudio.xyz/api.php

适用说明:X-Forwarded-Proto https 会让登录接口在 Cookie 上追加 Secure 属性;proxy_read_timeout 300s 用于兜住上游偶发的长耗时(实测热歌榜曾 180 秒超时,属上游问题,超时阈值给足只影响等待体验)。

⚠️ 注意:反向代理场景下不要把宿主端口做成 0.0.0.0 暴露,否则 https://IP:8136 可绕过 TLS 直达服务。上面的 127.0.0.1:8136:8787 写法即限定本机访问。

6.3 模板 C:上游故障时的备用源配置(4 核 4 GB、50 GB 磁盘、多人共用)

    environment:
      - PASSWORD=q1w2e3r4
      - API_BASE_URL=https://music-api.example.com/api.php

适用说明:当默认 GD音乐台持续返回 Challenge/超时(实测出现过搜索 6.45 秒、热歌榜 180 秒超时),换自建或备用聚合 API 是唯一根治手段。切换后必须重建容器并做一次搜索冒烟:

docker compose up -d --force-recreate
curl -s -b cookies.txt --max-time 60 \
  "http://127.0.0.1:8136/proxy?types=search&source=netease&name=test&count=1&pages=1&nocache=true" \
  -w "\nHTTP %{http_code}\n"

预期输出形态:

HTTP 200

6.4 三套模板对照

维度A 家庭最小B 反代 HTTPSC 备用源
硬件建议1 核 1 GB / 10 GB2 核 2 GB / 20 GB SSD4 核 4 GB / 50 GB
端口暴露内网直连 8136仅回环 + Nginx 443内网/反代均可
口令必设必设 + Secure Cookie必设
language不加不加不加
适用人数1-3 人家庭/小团队多人共用
主要风险内网横向访问证书续期备用源自建成本

七、常见问题

Q1:改了 PASSWORD,但用新口令还是登不进去?

只执行了 restart。restart 不重读编排,必须 docker compose up -d --force-recreate。用 docker inspect 确认容器里的 PASSWORD 已是新值。

Q2:想改容器内端口 8787 到别的端口,设 PORT=9000 没用?

入口脚本硬编码 export PORT=8787 覆盖了环境变量(见 5.4)。正确做法是改编排文件 ports 左侧(宿主端口),容器内保持 8787。

Q3:设了 language=ENG,界面仍是中文?

Docker 形态下页面由 Express 直接发静态文件,绕过 Wrangler 的注入中间件,实测 8787 页面 SITE_LANGUAGE 计数为 0、8788 内部请求计数为 1(见 5.3)。这是部署形态限制,不是配置错误。

Q4:搜索有时 6 秒、有时 180 秒超时,是配置问题吗?

不是。耗时几乎全部花在上游聚合 API。实测同一部署:搜索结果 6.45 秒返回,热歌榜 playlist 接口 180 秒超时(HTTP 000)。调 API_BASE_URL 换源,或在反代侧把超时放宽以避免网关先断。

Q5:/api/storage 返回 401?

Cookie 缺失或已过期(48 小时)。重新 POST /api/login 拿 Cookie;注意未登录时 /proxy、/api/*、/palette 都返回 401 JSON,只有页面请求才 302 跳登录。

Q6:想让收藏持久化到别的位置?

只能通过 volumes 改宿主侧路径(-v /data1/solara:/data);容器内路径 /data 由 DATA_DIR 固定,不建议改。

Q7:缓存能不能关掉或调短?

没有对应环境变量。要强制回源,在请求里加 nocache=true;要彻底禁用缓存需改源码(server/routes/proxy.js 的 cache.set 调用)后重建镜像。

八、参考链接