Funkwhale 配置参数详解

Funkwhale 2.0.12 三层配置体系拆解:环境变量/设置推导/动态偏好,FUNKWHALE_* 全变量分组详解,NGINX_MAX_BODY_SIZE 与实例名偏好改值实测、consolemail 密码重置邮件全链验证。

Funkwhale音频流媒体音乐流媒体2026/10/29

Funkwhale 配置参数详解

Funkwhale 2.0.12 的配置不是一份平铺的 conf 文件,而是「环境变量 → Django 设置推导 → 动态偏好」三层体系。本文把 2.0.12 实际读取的全部关键环境变量按职责逐组拆解,每组都给出 2.0.12 发行版源码依据与本站实例的真实改值/验证记录——包括 NGINX_MAX_BODY_SIZE 改值生效、实例名偏好改值、密码重置邮件渗透主机名等实测。

部署过程与编排文件见《Funkwhale Docker Compose 部署实战》,定位与选型见《Funkwhale 介绍与选型指南》;日常管理动作见《Funkwhale 常用命令与运维实战(核心篇)》。

一、配置体系三层结构

1.1 三层总览

层载体生效方式典型例子
环境变量层.env 文件(编排注入)需重建对应服务FUNKWHALE_HOSTNAME、NGINX_MAX_BODY_SIZE
设置推导层config/settings/common.py 内部逻辑由变量推导,不可直接改FUNKWHALE_URL = FUNKWHALE_PROTOCOL://FUNKWHALE_HOSTNAME
动态偏好层数据库(dynamic_preferences)保存即生效,无需重启实例名称/描述、默认上传配额

三层最容易混淆的点:「实例名称」「实例描述」「注册开放与否」这类运营参数不在 .env 里,它们存数据库、在管理界面即时可改;.env 里的 FUNKWHALE_HOSTNAME 则是技术身份,改动会撕裂联邦身份。

💡 技术拓展:dynamic_preferences 是什么? 这是 Django 生态的一个「把配置存进数据库」的库,Funkwhale 用它承载全部运营侧配置(偏好注册表按 区块__名称 寻址,如 instance__name)。它与 .env 的分工是:运维视角的机器配置走环境变量,管理员视角的站点配置走偏好——后文 2.3 节有一个两面对照的实测。

1.2 读取机制与 ENV_FILE

Funkwhale 用 django-environ 读取环境变量,设置文件里有一个容易被忽略的入口:

ENV_FILE = env_file = env("ENV_FILE", default=None)
...
    env.read_env(env_file)

即支持通过 ENV_FILE 变量指定一个额外文件再读一层(官方裸机安装脚本正是用 ENV_FILE=/srv/funkwhale/config/.env 传配置)。Docker 编排下则由 env_file: .env 直接把变量注入服务进程,两种途径等价。本文所有变量名均来自 2.0.12 发行版 config/settings/common.py 与 production.py 的实际读取行,不是官方文档的二手转述。

1.3 生产模式的两个硬性前置

config/settings/production.py(默认 DJANGO_SETTINGS_MODULE=config.settings.production)在导入时直接读两个变量,缺失即抛异常拒绝启动:

SECRET_KEY = env("DJANGO_SECRET_KEY")

数据库 URL 同理(见第四节)。这是 2.0.12 生产模式的「钥匙检查」:DJANGO_SECRET_KEY 不设,服务起不来。

二、实例身份组:FUNKWHALE_HOSTNAME 与它的推导链

2.1 变量清单与推导逻辑

变量默认值说明
FUNKWHALE_HOSTNAME无默认,必填实例对外主机名,如 pod.example.io。官方警告:上线后不可更改,否则联邦身份断裂
FUNKWHALE_PROTOCOLhttps对外访问协议,http 或 https
FUNKWHALE_URL无可整体替代上面两者(从 URL 解析出主机名与协议),二者取其一
FUNKWHALE_HOSTNAME_PREFIX / FUNKWHALE_HOSTNAME_SUFFIX无开发态 traefik 场景的主机名拼装,生产不用
FEDERATION_HOSTNAMEFUNKWHALE_HOSTNAME联邦层对外主机名(可分离,一般不动)
FEDERATION_SERVICE_ACTOR_USERNAMEservice实例服务账号的用户名
FUNKWHALE_SPA_HTML_ROOTFUNKWHALE_URL前端页面根地址(API 注入页面 meta 用)
FUNKWHALE_SPA_HTML_CACHE_DURATION900SPA 页面缓存秒数
FUNKWHALE_EMBED_URLFUNKWHALE_URL + "/embed.html"可嵌入播放器地址

主机名解析的优先级(2.0.12 源码节选,真实分支顺序):

FUNKWHALE_HOSTNAME = None
if FUNKWHALE_HOSTNAME_PREFIX and FUNKWHALE_HOSTNAME_SUFFIX:
    FUNKWHALE_HOSTNAME = "{}.{}".format(FUNKWHALE_HOSTNAME_PREFIX, FUNKWHALE_HOSTNAME_SUFFIX)
else:
    try:
        FUNKWHALE_HOSTNAME = env("FUNKWHALE_HOSTNAME")
        FUNKWHALE_PROTOCOL = env("FUNKWHALE_PROTOCOL", default="https")
    except Exception:
        FUNKWHALE_URL = env("FUNKWHALE_URL")
        _parsed = urlsplit(FUNKWHALE_URL)
        FUNKWHALE_HOSTNAME = _parsed.netloc
        FUNKWHALE_PROTOCOL = _parsed.scheme

FUNKWHALE_URL = f"{FUNKWHALE_PROTOCOL}://{FUNKWHALE_HOSTNAME}"

2.2 主机名的渗透范围实测

本实例设 FUNKWHALE_HOSTNAME=funkwhale.lab、FUNKWHALE_PROTOCOL=http,即 FUNKWHALE_URL=http://funkwhale.lab。实测这个值出现在至少四个互不相干的位置:

  1. 联邦对象 ID:曲目的艺术家对象 fid 为 http://funkwhale.lab/federation/music/artists/99209b21-...(API 实测回显);
  2. 密码重置邮件链接:http://funkwhale.lab/auth/password/reset/confirm?uid=...(第八节实测全文);
  3. 导入结果回显 URL:http://funkwhale.lab/library/a2b1e8f7-.../upload?import=ops-import-002(部署篇 5.3 节实测);
  4. front 服务内置 Web 服务器的配置:/manifest.json 请求被 302 到 http://funkwhale.lab/api/v2/instance/spa-manifest.json(导出 front 运行配置实测可见)。

第 4 项说明一个重要机制:front 的配置里烘焙了 FUNKWHALE_HOSTNAME,改它必须重建 front 服务(docker compose up -d front),只重启不重建不会重新渲染。

2.3 对照实验:实例名称在偏好层

FUNKWHALE_HOSTNAME 是技术身份,而访问者看到的「站点名称」属于动态偏好。在 api 服务内实测修改:

from dynamic_preferences.registries import global_preferences_registry
m = global_preferences_registry.manager()
print('before:', m['instance__name'])
m['instance__name'] = 'Funkwhale Lab Pod'
print('after :', m['instance__name'])

实测输出:

before:
after : Funkwhale Lab Pod

不重启任何服务,nodeinfo 端点立即反映:

curl -s http://127.0.0.1:8140/api/v1/instance/nodeinfo/2.0/ | python3 -c "import json,sys; print(json.load(sys.stdin)['metadata'].get('nodeName'))"

实测输出:

nodeinfo.nodeName = Funkwhale Lab Pod

同样的改动在管理界面(/api/admin/ → 动态偏好)里点几下也能完成——这正是「偏好层即时生效」的含义。

三、Web 服务组:监听、并发与上传上限

变量默认值说明
FUNKWHALE_API_IP127.0.0.1API 服务监听地址(Docker 内应为 0.0.0.0)
FUNKWHALE_API_PORT5000API 监听端口
FUNKWHALE_WEB_WORKERS4API 并行工作进程数,官方编排传给 gunicorn
FUNKWHALE_FRONTEND_PATH无前端 dist 目录,裸机安装用;Docker 下 front 自带前端文件,本文栈实测未消费该变量(2.0.12 源码 grep 无引用)
NGINX_MAX_BODY_SIZE100Mfront 内置 Web 服务器的单请求体积上限(Web 上传音乐的上限)
REVERSE_PROXY_TYPEnginx反代类型(影响受保护文件的头处理)
PROTECT_FILES_PATH/_protected受保护文件的内部路径前缀

3.1 监听与并发实测

FUNKWHALE_WEB_WORKERS=2 + FUNKWHALE_API_PORT=5000 的生效证据在 api 服务启动日志第一行:

+ exec gunicorn config.asgi:application --workers 2 --worker-class uvicorn.workers.UvicornWorker --bind 0.0.0.0:5000

FUNKWHALE_API_IP=0.0.0.0 在 Docker 栈下是必需的:宿主机端口映射要靠它把 5000 暴露到网络命名空间边界。裸机安装的官方默认 127.0.0.1(由系统反代转发)则相反。

3.2 NGINX_MAX_BODY_SIZE 改值实测

把 .env 中 NGINX_MAX_BODY_SIZE=100M 改为 50M 后 docker compose up -d front 重建,导出运行中的 Web 服务器配置:

docker exec funkwhale-2.0.12-front nginx -T 2>/dev/null | grep -m2 'client_max_body_size'

实测输出:

    client_max_body_size 50M;
        client_max_body_size 50M;

两处命中(全局 server 块 + /api/ 反代块),改值生效。随后恢复 100M 并重建,复测回到基线:

    client_max_body_size 100M;

结论:Web 上传大文件报 413 时,先查这个变量;它是 front 启动时渲染进配置的,改完必须重建 front 服务。

3.3 受保护文件链路实测

原位导入的音乐文件的播放链路是:API 校验权限后,把请求转交给 front 的内部路径。front 运行配置实测片段:

location /_protected/music/ {
    # This is an internal location that is used to serve
    # local music files once correct permission / authentication
    # has been checked on API side.
    # Set this to the same value as your MUSIC_DIRECTORY_PATH setting.
    internal;
    alias   /music/;
    add_header Access-Control-Allow-Origin '*';
}

internal 意味着外部直接访问 /_protected/music/... 一律 404,只有 API 发起内部跳转才能取到文件——这就是 PROTECT_FILES_PATH 与 MUSIC_DIRECTORY_PATH 两个变量必须对齐的原因。

四、数据库组:五件套与 URL 组装

变量默认值说明
DATABASE_HOSTlocalhost数据库主机(Docker 内为服务名 postgres)
DATABASE_PORT5432数据库端口
DATABASE_NAMEfunkwhale库名
DATABASE_USERfunkwhale用户名
DATABASE_PASSWORDfunkwhale密码
DATABASE_URL由五件套自动拼装完整连接串,显式提供时整体覆盖五件套
DB_CONN_MAX_AGE0连接最大复用秒数,0 为每次请求后关闭

五件套到 URL 的组装逻辑(2.0.12 源码节选):

DATABASE_URL = env.db(
    "DATABASE_URL",
    (
        f"postgres:"
        f"//{DATABASE_USER}:{DATABASE_PASSWORD}"
        f"@{DATABASE_HOST}:{DATABASE_PORT}"
        f"/{DATABASE_NAME}"
    ),
)

两点必须写进记忆:

  • 2.0.3 起官方要求 Docker 部署显式补齐 DATABASE_HOST/DATABASE_USER/DATABASE_NAME 三个变量——从 1.4 时代沿用下来的 .env 缺这三项,升 2.x 后数据库连不上。这是官方 changelog 里少见的「不填必炸」级要求;
  • DATABASE_URL 一旦显式提供,优先级高于五件套。两种写法二选一,混着写会以 URL 为准,排查连接问题时先确认没有残留的 DATABASE_URL。

五、缓存与异步队列组:一个 Redis 跑两个角色

变量默认值说明
CACHE_URLredis://127.0.0.1:6379/0Django 缓存(含会话)连接串
CELERY_BROKER_URL取 CACHE_URLCelery 消息中间件连接串
REDIS_PASSWORD无编排文件注入 Redis 服务端 --requirepass 用(API 本体不读它)
CELERYD_CONCURRENCY1worker 并行进程数
LOGLEVELinfo(受 DJANGO_DEBUG 影响)日志级别,worker/beat 的 --loglevel 直取此值

本实例的分工设计(.env 实值):

CACHE_URL=redis://:fw_redis_2o12_Passw0rd@redis:6379/0
CELERY_BROKER_URL=redis://:fw_redis_2o12_Passw0rd@redis:6379/1

同一 Redis 用 0/1 两个逻辑库隔离缓存与队列:缓存数据丢失可重建,队列消息丢了就是任务丢失,物理隔离字段但不隔离实例,备份与连接管理都只需要面对一个 Redis。

CELERYD_CONCURRENCY=2 与 LOGLEVEL=info 的生效证据:worker 启动后任务日志里的执行线程名为 ForkPoolWorker-1/ForkPoolWorker-2,beat 启动行为 exec celery '--app=funkwhale_api.taskapp' beat '--loglevel=info'(均为 docker logs 实测)。

六、存储目录组:MEDIA、STATIC 与原位导入双路径

变量默认值说明
MEDIA_ROOT发行版内 media/上传文件、封面、头像落点(必须持久化)
MEDIA_URLFUNKWHALE_URL + "/media/"媒体访问 URL 前缀
STATIC_ROOT发行版内 staticfiles/collectstatic 产物目录(管理后台样式等)
MUSIC_DIRECTORY_PATH无服务进程视角的原位音乐挂载点,Docker 推荐固定 /music,末尾不带斜杠
MUSIC_DIRECTORY_SERVE_PATH同 MUSIC_DIRECTORY_PATH宿主机视角的真实目录
MUSIC_USE_DENORMALIZATIONtrue音乐数据反规范化缓存开关

原位导入双路径是 Docker 部署最容易迷糊的一组:

  • MUSIC_DIRECTORY_SERVE_PATH=/opt/.../data/music:文件在宿主机上的真实位置,供编排文件挂载用;
  • MUSIC_DIRECTORY_PATH=/music:挂载后文件在 api/worker/front 服务内的路径, Funkwhale 数据库里登记的 source 就是它(数据库实测:file:///music/01 - Tone 440Hz.mp3)。

导入命令会先校验路径约定(部署篇 5.3 节实测首行 Checking imported paths against settings.MUSIC_DIRECTORY_PATH),播出的最后一跳由 3.3 节的 /_protected/music/ internal 路径完成。

STATIC_ROOT 的教训复述一遍:迁移完成后必须执行 collectstatic(实测 174 个文件收集后管理后台样式才正常),否则轻则无样式、重则被 SPA 兜底页的假 200 糊弄(部署篇 3.2 节有完整排障链)。

七、安全与管理入口组

变量默认值说明
DJANGO_SECRET_KEY无,必填会话/令牌签名密钥,生产必须换成高强度随机值
DJANGO_SETTINGS_MODULEconfig.settings.production设置模块开关(local 为开发态)
DJANGO_ADMIN_URL^api/admin/Django 管理后台路径(正则,改掉可减少扫描面)
SESSION_COOKIE_AGE3600 * 25 * 60(540 万秒,约 62.5 天)会话 cookie 有效期
THROTTLING_ENABLEDtrue请求节流总开关
THROTTLING_RATES{}各作用域的自定义限速字典
ACCOUNT_LOGIN_METHODS{"email", "username"}登录凭证类型
ACCOUNT_EMAIL_VERIFICATION_ENFORCEfalse强制邮箱验证后才可用(需先配好邮件服务)
ACCOUNT_USERNAME_BLACKLIST[]保留用户名黑名单

DJANGO_ADMIN_URL=^api/admin/ 的生效实测:http://192.168.146.128:8138/api/admin/login/ 返回 200(登录页)。注意这是 Django 的管理后台(数据库级运维入口),与 Web 界面里管理员侧边栏的「管理」不是同一套——后者是 Funkwhale 自带的应用管理页。

八、邮件组:consolemail 实测

变量默认值说明
EMAIL_CONFIGconsolemail://邮件连接串。可选:consolemail://(打印到日志)、smtp://、smtp+ssl://、smtp+tls://,均可带 user:pass@host:port
DEFAULT_FROM_EMAILFunkwhale <noreply@{主机名}>系统邮件发件人
SERVER_EMAIL同 DEFAULT_FROM_EMAIL错误报告类邮件发件人
EMAIL_SUBJECT_PREFIX[Funkwhale] 邮件主题前缀

测试实例用 consolemail://(不发真邮件,内容打印进 api 日志)。实测触发一次密码重置:

curl -s -X POST http://127.0.0.1:8140/api/v2/auth/password/reset/ \
  -H 'Content-Type: application/json' -d '{"email":"admin@funkwhale.lab"}'

API 回显:

{"detail":"Password reset e-mail has been sent."}

api 日志中捕获到的邮件全文:

Subject: Password reset on funkwhale_api
From: noreply@funkwhale.lab
To: admin@funkwhale.lab
Date: Wed, 28 Oct 2026 16:06:26 +0000
Message-ID: <179320358695.8.14762155340990056427@e4bd20bc3a28>


You're receiving this e-mail because you requested a password reset for your =
user account at funkwhale.lab.

Please go to the following page and choose a new password:
http://funkwhale.lab/auth/password/reset/confirm?uid=3DMQ&token=3Ddh622q-a92c=
ecf66204f8896fd3aeb9f3d20441
Your username, in case you've forgotten: admin

Thanks for using our site!

The funkwhale.lab team

一封邮件同时验证了三个变量:EMAIL_CONFIG(内容落日志)、DEFAULT_FROM_EMAIL(From 行)、FUNKWHALE_HOSTNAME(正文链接)。生产换 smtp+tls://user:pass@smtp.example.com:587 即可,格式不变。

九、可选集成组速览

以下变量默认全关,按需启用(2.0.12 源码均含对应读取行):

  • Typesense 全文搜索:TYPESENSE_API_KEY(设了即启用)、TYPESENSE_HOST(默认 typesense)、TYPESENSE_PORT(默认 8108)、TYPESENSE_PROTOCOL(默认 http)、TYPESENSE_NUM_TYPO(默认 5)。未启用时启动日志固定打印 Typesense is not activated. You can enable it by setting the TYPESENSE_API_KEY env variable.(本实例实测可见);
  • S3 兼容对象存储:AWS_ACCESS_KEY_ID、AWS_SECRET_ACCESS_KEY、AWS_STORAGE_BUCKET_NAME、AWS_S3_ENDPOINT_URL、AWS_S3_REGION_NAME、AWS_S3_CUSTOM_DOMAIN、AWS_S3_URL_PROTOCOL(默认 https:)、AWS_LOCATION(默认空)、AWS_DEFAULT_ACL——配齐即把媒体与附件从本地磁盘切到对象存储;
  • LDAP 认证:LDAP_SERVER_URI、LDAP_BIND_DN、LDAP_BIND_PASSWORD、LDAP_SEARCH_FILTER、LDAP_ROOT_DN、LDAP_GROUP_DN 等,启用后登录走目录服务;
  • 元数据源:MUSICBRAINZ_HOSTNAME(默认 musicbrainz.org,可指向自建同步站规避限流);
  • Subsonic 兼容层:SUBSONIC_DEFAULT_TRANSCODING_FORMAT(默认 mp3);
  • 监控:FUNKWHALE_SENTRY_DSN(设了即上报错误到 Sentry)、FUNKWHALE_SENTRY_SR(默认 0.25,追踪采样率);
  • 插件:FUNKWHALE_PLUGINS(逗号分隔启用列表)、FUNKWHALE_PLUGINS_PATH。2.0.12 默认启用 scrobbler、listenbrainz、maloja 三个听歌记录插件(启动日志实测);
  • 附件治理:ATTACHMENTS_UNATTACHED_PRUNE_DELAY(默认 86400 秒,未挂接的附件一天后由周期任务清理)。

十、改配置的标准动作与禁忌

  1. 环境变量改动:改 .env → docker compose up -d(受影响服务会按需重建)→ 验证。凡是被 front 烘焙进配置的变量(NGINX_MAX_BODY_SIZE、FUNKWHALE_HOSTNAME)必须重建而非重启;
  2. 偏好改动:管理界面或 API 即时生效,无需重启(2.3 节实测);
  3. 禁忌一:FUNKWHALE_HOSTNAME 上线后不改。联邦 actor ID、对象 fid 全部以它为根,改了等于给实例换身份,既有联邦关系全部作废;
  4. 禁忌二:DJANGO_SECRET_KEY 上线后不换。换了之后全部会话、OAuth 令牌、密码重置链接作废(必须换时安排在维护窗口);
  5. 禁忌三:数据库五件套与 DATABASE_URL 不混写,DATABASE_URL 优先级更高,排查连接问题先查残留。

信息来源:变量名、默认值与代码行为均取自 funkwhale/api 2.0.12 发行版 config/settings/common.py、config/settings/production.py 与 front 2.0.12 内置 Web 服务器配置的实测导出(2026-10-28);改值实验(NGINX_MAX_BODY_SIZE、instance__name、密码重置邮件)为本站实例实机操作记录。