Solara 2.1.6 Docker Compose 部署指南
用 Docker Compose 在 Ubuntu 22.04 单机部署 Solara 2.1.6 自托管网页音乐播放器:编排文件编写、口令认证、SQLite 数据持久化、搜索/歌词/封面/播放直链与 Range 流全链路实测,含 compose 项目名规范化与上游 API 波动等真实排障结论。
Solara 2.1.6 Docker Compose 部署指南
目标:用 Docker Compose 在单台 Ubuntu 22.04 服务器上从零部署 Solara 2.1.6「光域」网页音乐播放器,完成「编排启动 → 口令认证 → 搜索/歌词/封面/播放直链 → 音频流 Range 播放 → 数据持久化」的完整验证链路。本文全部命令与回显已在 Docker Engine 29.9.2(Compose v5.6.1)环境实测通过。
一、环境信息
- 操作系统:Ubuntu 22.04.5 LTS(x86_64)
- Docker Engine:29.9.2,Docker Compose 插件 v5.6.1
- Solara 版本:2.1.6(2026-09-11 发布,官方最新稳定 tag,版本信息查询日期 2026-10-25)
- 官方镜像:
ghcr.io/akudamatata/solara:2.1.6(linux/amd64,765020763 字节 ≈ 765 MB,声明暴露8787/tcp) - 端口规划:宿主
8136→ 容器8787(容器内端口由入口脚本固定为 8787,改宿主侧映射即可) - 编排目录:
/opt/solara/(示例路径,含docker-compose.yml与data/) - 独立网络:
solara-net(bridge,网段10.89.65.0/24,容器固定 IP10.89.65.10) - 上游音乐 API:
https://music-api.gdstudio.xyz/api.php(GD音乐台,项目默认聚合源) - 演示访问口令:
q1w2e3r4 - 实测资源基线:空载 CPU 0.88%、内存 195.4 MiB、进程数 71
二、镜像选型说明
Solara 官方只发布单一镜像,没有 slim/alpine 等变体,选型集中在仓库来源与标签策略两点:
| 项目 | 取值 | 说明 |
|---|---|---|
| 官方镜像仓库 | ghcr.io/akudamatata/solara | GitHub Container Registry,与源码仓库同属一个组织,唯一官方发布渠道 |
| 精确版本标签 | 2.1.6 | 与仓库 tag v2.1.6 对应,生产推荐,升级可控、回显可复现 |
| 滚动最新标签 | latest | 跟随最新稳定版,docker compose pull 即升级,但版本不可复现 |
| 第三方重打包镜像 | aexus/solara-music | 不要使用:仅 latest 一个标签、体积 51.9 MB(官方 765 MB)、2025-11-16 后停更,与官方镜像差异巨大且无人维护 |
镜像关键元数据(docker image inspect 实测):
docker image inspect ghcr.io/akudamatata/solara:2.1.6 \
--format 'Size={{.Size}} {{.Os}}/{{.Architecture}} Exposed={{json .Config.ExposedPorts}} Entrypoint={{json .Config.Entrypoint}}'
实测输出:
Size=765020763 linux/amd64 Exposed={"8787/tcp":{}} Entrypoint=["/app/docker-entrypoint.sh"]
- 镜像体积约 765 MB,主要来自 Node.js 运行时 + Wrangler(Cloudflare Pages 本地开发服务器)依赖,比轻量静态站镜像大得多
- 入口固定为
/app/docker-entrypoint.sh:先写/app/.dev.vars,再拉起后台 Wrangler(127.0.0.1:8788)与前台 Express(0.0.0.0:8787) - 只暴露 8787 一个端口,8788 仅在容器内部回环监听,不对外映射
💡 说明:选精确标签
2.1.6而非latest,保证本文全部回显可复现;latest适合个人尝鲜,但一旦上游推新版本,同样的编排文件行为可能变化。
三、前置条件
- 服务器已安装 Docker Engine 与 Compose 插件。验证:
docker version --format 'Engine {{.Server.Version}}'
实测输出:
Engine 29.9.2
docker compose version
实测输出:
Docker Compose version v5.6.1
- 端口未被占用(本文示例 8136):
ss -ltn | grep -q ':8141' && echo "8141 已被占用" || echo "8141 空闲"
实测输出:
8141 空闲
💡 说明:上面用 8141 做空闲探测示例,部署时把检查目标换成你实际要用的宿主端口即可。若已被占用,改编排文件的
ports左侧值,或先停掉占用方。
- 宿主与上游 API 网络可达。Solara 自身不提供音乐数据,搜索/歌词/直链全部依赖上游聚合 API,部署前先确认出网正常:
curl -s -o /dev/null -w "HTTP %{http_code} time=%{time_total}s\n" --max-time 15 \
"https://music-api.gdstudio.xyz/api.php?types=search&source=netease&name=test&count=1&pages=1"
实测输出:
HTTP 200 time=2.184193s
- 磁盘空间充足(镜像约 765 MB + 数据目录):
df -h /opt
实测输出:
Filesystem Size Used Avail Use% Mounted on
/dev/sda2 443G 72G 354G 17% /
四、部署步骤
本章按「建目录与编排 → 启动 → 状态确认 → 认证验证 → 上游接口验证 → 音频流验证 → 重启持久化」完成 0→1 部署。预期结果:服务健康运行、口令登录生效、搜索与播放链路全通、重启后数据不丢。
4.1 创建部署目录
mkdir -p /opt/solara && cd /opt/solara
实测输出:无输出(目录创建成功)。
💡 说明:编排目录内只需
docker-compose.yml一个文件,运行时数据由 Compose 自动创建在./data/。
4.2 编写编排文件
/opt/solara/docker-compose.yml 完整内容:
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
- language=ENG
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
逐项说明:
name:显式指定 Compose 项目名。⚠️ Docker 会把项目名规范化为「小写字母 + 数字 + 连字符」,点号会被剥除——若写成solara-v2.1.6-stack,实际项目名会变成solara-v216-stack。本文按规范化后的形态直接写solara-v216-stack,避免后续docker compose命令与网络/容器命名对不上:image:精确版本标签2.1.6,与本文回显一致container_name:固定实例名,便于docker logs/docker exec定位restart: unless-stopped:宿主重启后自动拉起,手动stop后不会被自动拉起init: true:官方建议项,用 tini 作为 1 号进程,解决 Node/Wrangler 双进程无法优雅响应 SIGTERM 导致的停止卡顿(否则docker stop要等超时才杀)ports:宿主 8136 → 容器 8787PASSWORD:访问口令,全站单一共享口令;留空即开放访问(任何人可直接进入播放器)API_BASE_URL:上游聚合 API 地址,默认即 GD音乐台;上游被墙/被 Challenge 时换备用地址language:界面语言,ENG为英文(Docker 形态实测不生效,原因见第七章)volumes:./data挂到/data,SQLite 数据库落在此处,是唯一需要持久化的目录networks:自定义 bridge 网络并固定容器 IP,便于后续与反向代理/其他服务同栈编排
4.3 启动服务
docker compose up -d
实测输出:
Network solara-v216-stack_solara-net Creating
Network solara-v216-stack_solara-net Creating
Network solara-v216-stack_solara-net Created
Network solara-v216-stack_solara-net Created
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
💡 说明:首次启动会先
pull约 765 MB 镜像,耗时取决于带宽;网络与容器各出现两行 Creating/Created 是 Compose 对项目级网络与容器描述符的正常输出。
4.4 状态与启动日志确认
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
STATUS 为 Up、PORTS 出现 0.0.0.0:8136->8787/tcp 即容器已正常监听。
docker logs solara-v2.1.6-app
实测输出:
Configuring environment variables for wrangler...
[env] PASSWORD = configured
[env] API_BASE_URL = configured
[env] language = configured
🌟 Solara (Cloudflare Pages + Wrangler local dev)
────────────────────────────────────────────────────
Port : 8787
Data dir : /data
Password : configuredq1w2e3r4
API URL : https://music-api.gdstudio.xyz/api.php
Language : ENG
────────────────────────────────────────────────────
Starting wrangler pages dev in background on port 8788...
Starting Node.js standalone server on port 8787...
[DB] SQLite initialized at /data/solara.db
(node:7) ExperimentalWarning: SQLite is an experimental feature and might change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
🌟 Solara Standalone Server
─────────────────────────────────────
Ready on http://0.0.0.0:8787
Password: ✓ configured
─────────────────────────────────────
关键三行解读:
[DB] SQLite initialized at /data/solara.db:服务端存储初始化成功,数据库落在挂载卷内Ready on http://0.0.0.0:8787:Express 前台已监听Starting wrangler pages dev in background on port 8788:Wrangler 代理进程已拉起(只在容器内回环监听)
⚠️ 安全警示(实测发现):启动横幅中的
Password : configuredq1w2e3r4把访问口令明文打进了日志。原因是入口脚本的展开写法${PASSWORD:+configured}${PASSWORD:-not set (open access)}在两个分支都非空时把configured与密码值直接拼接。生产环境务必限制docker logs的可见范围(见《Solara 日常运维手册》),或改用日志采集白名单过滤该行。
💡 说明:
ExperimentalWarning: SQLite is an experimental feature是 Node.js 内置node:sqlite模块的实验特性提示,不影响功能;Solara 用内置模块替代 Cloudflare D1,因此无需任何原生编译依赖。
4.5 访问控制验证
未登录访问首页,应被重定向到登录页:
curl -s -o /dev/null -w "GET / -> %{http_code} redirect=%{redirect_url}\n" http://127.0.0.1:8136/
实测输出:
GET / -> 302 redirect=http://127.0.0.1:8136/login
用错误口令登录,应返回 401:
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"
实测输出:
{"success":false}
HTTP 401
用正确口令(本文演示口令 q1w2e3r4)登录,应下发认证 Cookie:
curl -s -D - -o /dev/null -X POST http://127.0.0.1:8136/api/login \
-H 'Content-Type: application/json' -d '{"password":"q1w2e3r4"}'
实测输出:
HTTP/1.1 200 OK
X-Powered-By: Express
Set-Cookie: auth=cTF3MmUzcjQ=; Max-Age=172800; Path=/; SameSite=Lax; HttpOnly
Content-Type: application/json; charset=utf-8
Content-Length: 16
ETag: W/"10-oV4hJxRVSENxc/wX8+mA4/Pe4tA"
Date: Sun, 25 Oct 2026 04:18:03 GMT
Connection: keep-alive
Keep-Alive: timeout=5
登录后带 Cookie 访问首页:
curl -s -c /opt/solara/cookies.txt -X POST http://127.0.0.1:8136/api/login \
-H 'Content-Type: application/json' -d '{"password":"q1w2e3r4"}' > /dev/null
curl -s -b /opt/solara/cookies.txt -o /dev/null -w "GET / (auth cookie) -> %{http_code}\n" http://127.0.0.1:8136/
实测输出:
GET / (auth cookie) -> 200
💡 说明:
auth=cTF3MmUzcjQ=是口令的 Base64 编码(q1w2e3r4→cTF3MmUzcjQ=),Cookie 有效期Max-Age=172800(48 小时),带HttpOnly防脚本读取,SameSite=Lax。吊销口令即等于吊销所有会话——改PASSWORD后重建容器,旧 Cookie 立即失效。
4.6 上游接口链路验证
搜索歌曲(关键词「周杰伦」,源 netease):
curl -s -b /opt/solara/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 search.json -w "HTTP %{http_code} bytes=%{size_download} time=%{time_total}s\n"
实测输出:
HTTP 200 bytes=1188 time=6.454419s
解析返回的曲目(节选字段):
python3 -c "
import json
d=json.load(open('search.json'))
for s in d[:5]:
print('-',s.get('name'),'/',','.join(s.get('artist',[])),'| id:',s.get('id'),'| source:',s.get('source'),'| pic_id:',s.get('pic_id'),'| lyric_id:',s.get('lyric_id'))
"
实测输出:
- 想你就写信 (Live) / 周杰伦,李硕,张鑫 | id: 509781655 | source: netease | pic_id: 109951163038292176 | lyric_id: 509781655
- 屋顶 / 周杰伦,温岚,吴宗宪 | id: 5257138 | source: netease | pic_id: 109951165671182684 | lyric_id: 5257138
- 布拉格广场 / 蔡依林,周杰伦 | id: 210049 | source: netease | pic_id: 109951172453712025 | lyric_id: 210049
- 默 (Live) / 李荣浩,周杰伦 | id: 1888354230 | source: netease | pic_id: 109951166537300832 | lyric_id: 1888354230
- 因为爱情 (Live) / 周杰伦,那英 | id: 490595315 | pic_id: 18892908300315027 | lyric_id: 490595315
取第一首的 ID 拉歌词:
curl -s -b /opt/solara/cookies.txt --max-time 60 \
"http://127.0.0.1:8136/proxy?types=lyric&id=509781655&source=netease" \
-o lyric.json -w "HTTP %{http_code} bytes=%{size_download} time=%{time_total}s\n"
实测输出:
HTTP 200 bytes=1411 time=1.380085s
歌词为标准的 LRC 文本({"lyric":"[00:00.00] 作词 : 方文山\n[00:01.00] 作曲 : 周杰伦\n[00:26.17]...),前端按时间戳逐行高亮。
取封面:
curl -s -b /opt/solara/cookies.txt --max-time 60 \
"http://127.0.0.1:8136/proxy?types=pic&id=109951163038292176&source=netease&size=300" \
-w "HTTP %{http_code} bytes=%{size_download} time=%{time_total}s\n"
实测输出:
HTTP 200 bytes=124 time=1.543759s
{"url":"https://p2.music.126.net/yD9vbpuILH-tqNRIaP640g==/109951163038292176.jpg?param=300y300","from":"music.gdstudio.xyz"}
取播放直链(128 kbps,强制跳过缓存以观察真实解析耗时):
curl -s -b /opt/solara/cookies.txt --max-time 120 \
"http://127.0.0.1:8136/proxy?types=url&id=509781655&source=netease&br=128&nocache=true" \
-w "HTTP %{http_code} bytes=%{size_download} time=%{time_total}s\n"
实测输出:
HTTP 200 bytes=234 time=2.900028s
{"url":"https://m801.music.126.net/20261025124313/c4588f6df5f923333c278975094650ea/jdymusic/obj/wo3DlMOGwrbDjj7DisKw/17432853135/a2d8/c265/26ad/fd9ff88a1226ec6b79a77ffca292d465.mp3","br":128,"size":3820077,"from":"music.gdstudio.xyz"}
⚠️ 音源说明(实测与 README 不符):README 宣称支持「网易云/QQ/酷狗」等数据源,实测
source=kuwo时上游直接返回 HTTP 400:{"detail":"Value of `source` is not supported."} HTTP 400当前实际可用的只有
netease。前端下拉仍保留netease/kuwo/joox/bilibili四项,选择非 netease 项会得到空结果或上述报错。高码率风险:同一首歌
br=320/br=999(FLAC)实测解析 115 秒超时(HTTP 000),br=128稳定在 3 秒内。生产建议默认音质用 128 或 192。
4.7 音频流播放验证
拿上一步返回的直链做 Range 探测(模拟播放器拖动进度条的分片请求):
curl -s -o /dev/null -H "Range: bytes=0-65535" \
-w "HTTP %{http_code} type=%{content_type} bytes=%{size_download} time=%{time_total}s\n" \
--max-time 60 "https://m801.music.126.net/20261025124313/c4588f6df5f923333c278975094650ea/jdymusic/obj/wo3DlMOGwrbDjj7DisKw/17432853135/a2d8/c265/26ad/fd9ff88a1226ec6b79a77ffca292d465.mp3"
实测输出:
HTTP 206 type=audio/mpeg bytes=65536 time=0.165539s
206 Partial Content + audio/mpeg 表示直链支持 Range 请求、可流式拖动播放;单次 64 KB 分片 0.17 秒返回,播放起播延迟很低。
4.8 重启与数据持久化验证
docker compose restart
实测输出:
Container solara-v2.1.6-app Restarting
Container solara-v2.1.6-app Started
重启后先写一条测试数据,再重启一次,验证数据落盘:
curl -s -b /opt/solara/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"}}'
实测输出:
{"d1Available":true,"updated":1}
读回该键:
curl -s -b /opt/solara/cookies.txt \
"http://127.0.0.1:8136/api/storage?keys=doc-check-2026-10-25"
实测输出:
{"d1Available":true,"data":{"doc-check-2026-10-25":"solara-2.1.6-ok"}}
再整体重启一次,读取全库确认既有数据仍在:
docker compose restart && sleep 4
curl -s -b /opt/solara/cookies.txt http://127.0.0.1:8136/api/storage
实测输出:
Container solara-v2.1.6-app Restarting
Container solara-v2.1.6-app Started
{"d1Available":true,"data":{"favorites:songs":"[{\"id\":\"5257138\",\"name\":\"屋顶\"}]","doc-check-2026-10-25":"solara-2.1.6-ok"}}
重启后数据完整保留。删除测试键:
curl -s -b /opt/solara/cookies.txt -X DELETE http://127.0.0.1:8136/api/storage \
-H 'Content-Type: application/json' -d '{"keys":["doc-check-2026-10-25"]}'
实测输出:
{"d1Available":true,"deleted":1}
五、数据持久化说明
所有需要落盘的状态都在 /data(宿主 ./data),实测目录结构:
ls -la /opt/solara/data/
实测输出:
total 112
drwxr-xr-x 3 root root 4096 Oct 25 11:43 .
drwxr-xr-x 7 root root 4096 Oct 25 12:18 ..
-rw-r--r-- 1 root root 4096 Oct 25 11:43 solara.db
-rw-r--r-- 1 root root 32768 Oct 25 12:18 solara.db-shm
-rw-r--r-- 1 root root 61832 Oct 25 12:18 solara.db-wal
drwxr-xr-x 5 root root 4096 Oct 25 11:43 v3
solara.db:SQLite 主库,两张表playback_store(播放状态/播放队列)与favorites_store(收藏列表),跨设备漫游的收藏与进度都在这里solara.db-wal/solara.db-shm:WAL 模式的日志与共享内存文件,备份时必须一起打包,只拷solara.db可能丢最近的写入v3/:Wrangler(miniflare)的持久化目录,含cache/、d1/、workflows/子目录;Docker 形态下前端读写走 Express+SQLite,不依赖该目录,但它仍由 Wrangler 进程创建,保留即可
实测整个目录仅 296 KB,体量极小,直接整目录打包即可完成备份。
💡 说明:
docker compose down只删除容器与网络,不删宿主./data;再次up -d后收藏与播放状态原样恢复(本文 4.8 节已实测验证)。
六、环境变量表
| 环境变量 | 默认值 | 是否生效 | 说明 |
|---|---|---|---|
PASSWORD | 空(开放访问) | 生效 | 全站访问口令;留空则任何人可直接进入,未登录访问首页不再重定向 |
API_BASE_URL | https://music-api.gdstudio.xyz/api.php | 生效 | 上游聚合 API 地址,搜索/歌词/封面/直链都经它 |
language | 空(中文) | 写入成功,Docker 形态界面不切换 | 界面语言,ENG 为英文;生效条件见第七章 |
PORT | 8787 | 不生效(被入口脚本覆盖) | 入口脚本显式 export PORT=8787,容器内端口固定,改宿主映射才是正解 |
HOST | 0.0.0.0 | 不生效(被入口脚本覆盖) | 同上,入口脚本 export HOST=0.0.0.0 |
DATA_DIR | /data | 不生效(被入口脚本覆盖) | 入口脚本 export DATA_DIR=/data,持久化目录固定为 /data |
WRANGLER_API_URL | http://127.0.0.1:8788 | 由入口脚本注入 | Express 转发 /proxy 时使用的内部地址,无需手工设置 |
⚠️ 实测提醒:
PORT/HOST/DATA_DIR虽然 Express 代码支持从环境变量读取,但容器入口脚本在启动前无条件export了固定值,因此这三个变量在 Docker 形态下改不动。要换对外端口只能改编排文件的ports左侧值;要换数据目录只能改volumes的映射目标(不建议,/data是镜像内约定路径)。
七、配置参数说明
7.1 PASSWORD 与开放访问
PASSWORD 决定是否启用认证。逻辑是:未配置(空)时全部放行;配置后校验 Cookie auth 是否等于口令的 Base64。
- 设置口令(本文做法):登录页输入口令 → 下发 48 小时 Cookie → 首页可访问
- 清空口令:删除编排文件中
PASSWORD行 →docker compose up -d重建 → 首页直接 200,日志横幅显示Password: ✗ not set (open access) - 改口令:改
PASSWORD值并重建容器,所有旧 Cookie 立即失效(Cookie 值随口令变化)
7.2 API_BASE_URL 与上游切换
默认指向 GD音乐台免费聚合 API。上游出现持续 Challenge/超时时,可换备用地址后重建容器:
# 编排文件中改为备用地址后重建
docker compose up -d --force-recreate
验证是否切换成功:
curl -s -b /opt/solara/cookies.txt --max-time 30 \
"http://127.0.0.1:8136/proxy?types=search&source=netease&name=test&count=1&pages=1" \
-w "\nHTTP %{http_code}\n"
实测输出(正常形态):
HTTP 200
⚠️ 故障注入实测:把
API_BASE_URL指向不可达地址并重建后,搜索接口返回502(Express 侧Upstream proxy error),页面搜索无结果。恢复为正确地址重建即恢复。
7.3 language 在 Docker 形态下不生效(docs vs reality)
这是本项目最值得注意的「文档声称支持、实测不生效」项:
- 官方 README 称 Docker 部署时设置
language=ENG即可切换英文界面 - 实测:入口脚本确实把
language写进了/app/.dev.vars(见第八章回显),Wrangler 侧的中间件也确实会在 HTML<head>注入window.SITE_LANGUAGE = "ENG" - 但 Docker 形态的页面由 Express 直接
sendFile返回(server/index.js的GET /与GET /login),绕过了 Wrangler 中间件,浏览器拿到的 HTML 里没有该注入 - 实测结果:经 8787 端口取回的页面中
window.SITE_LANGUAGE出现次数为 0;直连容器内 8788 端口(Wrangler)取回的页面出现次数为 1 - 结论:英文界面只在 Cloudflare Pages 部署形态生效;Docker 形态保留
language=ENG不报错但界面仍是中文
7.4 缓存参数
/proxy上游响应缓存 5 分钟:搜索结果、歌词、直链共用;命中时响应头X-Cache-Status: HIT- 缓存排除:结果为空(
[])、含"error"/"status":0的响应不写缓存;URL 中带nocache=true时强制回源 /palette封面调色板结果缓存 1 小时,减少对封面图的重复下载与解码
验证缓存是否命中:
curl -s -b /opt/solara/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 /opt/solara/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
八、配置文件挂载说明
Solara 没有外部配置文件(不像 Navidrome 的 TOML/Redis 的 conf),全部配置项只有上面 3 个环境变量。容器内存在一个由入口脚本自动生成的文件 /app/.dev.vars,供 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"
逐行说明:
- 三行分别对应编排文件
environment的三个变量;值为空或未设置的变量不会写入该文件 - 文件每次容器启动时被清空重写(入口脚本先
: > "$DEV_VARS"),因此手工修改容器内该文件在重启后会丢失 - 若确有特殊需求要自定义,正确的做法是改编排文件的
environment,而不是挂载覆盖该文件——挂载覆盖会与入口脚本的「清空重写」逻辑冲突,导致变量丢失
💡 说明:这就是本项目「配置即环境变量」的设计,也意味着没有配置文件热加载——任何参数变更都要
docker compose up -d(必要时--force-recreate)重建容器。
九、等价 docker run 命令
不使用 Compose 时,先建好独立网络(与编排中的 solara-net 等价,本文实测用 10.89.66.0/24 避免与已部署栈冲突):
docker network create -d bridge --subnet 10.89.66.0/24 solara-run-net
实测输出:
bdde77a69d0188792ce2dbb0ffb0f66a3825ec74bd11601f11f8b91ab204fe76
再启动容器(已实测跑通,宿主端口用空闲的 8141):
docker run -d \
--name solara-v2.1.6-app \
--restart unless-stopped \
--init \
-p 8141:8787 \
-e PASSWORD=q1w2e3r4 \
-e API_BASE_URL=https://music-api.gdstudio.xyz/api.php \
-e language=ENG \
-v /opt/solara/data:/data \
--network solara-run-net \
ghcr.io/akudamatata/solara:2.1.6
实测输出:
506ce0e73ea150b6e79864111414e6262088aa4fb0cb6194e31a76c644d8a47c
容器创建后探活:
curl -s -o /dev/null -w "GET / -> %{http_code} redirect=%{redirect_url}\n" http://127.0.0.1:8141/
实测输出:
GET / -> 302 redirect=http://127.0.0.1:8141/login
启动日志尾部(与 Compose 形态一致):
🌟 Solara Standalone Server
─────────────────────────────────────
Ready on http://0.0.0.0:8787
Password: ✓ configured
─────────────────────────────────────
参数与编排文件一一对应:-p 端口映射、-e 环境变量、-v 数据卷、--network 接入网络、--init 对应 init: true。长期运行仍建议用 Compose——编排即文档,改动可追溯。
十、常用运维命令
以下命令均在 /opt/solara/(编排文件所在目录)执行。
查看实时日志:
docker logs -f --tail 100 solara-v2.1.6-app
查看服务状态:
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
重启服务(改环境变量后必须用 up -d 重建,restart 不会重读编排):
docker compose restart
实测输出:
Container solara-v2.1.6-app Restarting
Container solara-v2.1.6-app Started
重建容器(环境变量/端口变更后):
docker compose up -d
实测输出(无变化时):
Container solara-v2.1.6-app Running
停用服务(保留数据):
docker compose down
实测输出:
Container solara-v2.1.6-app Stopping
Container solara-v2.1.6-app Stopped
Container solara-v2.1.6-app Removing
Container solara-v2.1.6-app Removed
Network solara-v216-stack_solara-net Removing
Network solara-v216-stack_solara-net Removed
再次启动:
docker compose up -d
实测输出:
Network solara-v216-stack_solara-net Creating
Network solara-v216-stack_solara-net Created
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
服务重启后探活:
curl -s -o /dev/null -w "GET /login -> %{http_code}\n" http://127.0.0.1:8136/login
实测输出:
GET /login -> 200
💡 说明:
down只删容器与项目网络,./data目录与其中的 SQLite 数据库完整保留;再次up -d即恢复全部收藏与播放状态。
升级到新版本:把编排文件 image 标签改为新 tag,然后:
docker compose pull && docker compose up -d
实测输出(pull 段):
Image ghcr.io/akudamatata/solara:2.1.6 Pulling
Image ghcr.io/akudamatata/solara:2.1.6 Pulled
回滚:改回上一版本 tag(如 2.1.5)后同样 docker compose up -d。实测 2.1.5 ↔ 2.1.6 互切后 data/ 数据无损(同一 SQLite 表结构)。
十一、常见问题
Q1:docker compose up -d 报端口已被占用(port is already allocated)?
先查占用方:ss -ltnp | grep 8136。占用方是别的服务就改编排文件 ports 左侧值;占用方是残留的旧 Solara 容器则 docker compose down 后重来(本文实测 down 会一并删掉项目网络,不会残留)。
Q2:编排里写 name: solara-v2.1.6-stack,为什么 docker network ls 里看到的是 solara-v216-stack_solara-net?
Docker 会把 Compose 项目名规范化为「小写字母+数字+连字符」,点号被剥除。solara-v2.1.6-stack → solara-v216-stack。验证方式:
docker compose config --format json | python3 -c "import json,sys; print('name=', json.load(sys.stdin)['name'])"
实测输出:
name= solara-v216-stack
建议直接按规范化后的名字写 name:。
Q3:搜索一直无结果,或提示接口繁忙?
按顺序排查:① 访问控制是否通过(未登录时 /proxy 返回 401 JSON {"error":"Unauthorized"});② 上游可达性(curl 直连 API_BASE_URL 看是否 200);③ 免费 API 波动——实测同日热歌榜接口曾 180 秒超时(HTTP 000),几分钟后恢复属常态;④ 换 source 试,实测仅 netease 可用。
Q4:选了「酷我」源报错?
实测上游已不支持:{"detail":"Value of source is not supported."}(HTTP 400)。这不是部署问题,属上游能力收缩,选回 netease 即可。
Q5:设置了 language=ENG,界面还是中文?
见第七章 7.3:Docker 形态下页面由 Express 直接发送、绕过 Wrangler 的 HTML 注入中间件,故不生效;仅 Cloudflare Pages 形态支持。这不是配置错误。
Q6:docker stop 要等十秒才停?
确认编排里 init: true 在位。缺少该项时容器内 Node + Wrangler 两个进程收不到 SIGTERM 传播,Docker 只能等默认 10 秒超时后 SIGKILL;加上后停止通常是秒级。
Q7:启动日志里能看到访问口令明文?
是的,属实测确认的缺陷(见 4.4 节安全警示)。缓解措施:限制 docker logs 访问权限、日志采集侧对 Password : 行做过滤、并把口令当作已泄漏凭据管理(在可信内网使用、不做外网直接暴露)。
Q8:容器启动后短暂出现 ECONNREFUSED 127.0.0.1:8788?
入口脚本启动 Wrangler 后 sleep 2 就拉起 Express,机器负载高时 Wrangler 可能尚未就绪,首个 /proxy 请求会命中连接拒绝,稍后自动恢复(实测重建后出现过一次,后续请求正常)。不需要干预;持续出现则查 docker logs 中 wrangler 的启动失败原因。
十二、参考链接
- 上游仓库:https://github.com/akudamatata/Solara
- 版本发布页:https://github.com/akudamatata/Solara/tags(v2.1.6 发布于 2026-09-11,查询日期 2026-10-25)
- 官方镜像:https://ghcr.io/akudamatata/solara
- 上游聚合 API(GD音乐台):https://music.gdstudio.xyz/
- 站内同专栏:《Solara 2.1.6 介绍与选型指南》、《Solara 2.1.6 配置参数详解》、《Solara 日常运维手册》
- 站内同类参照:《Navidrome 0.64.2 Docker Compose 部署指南》