Fluxer 2026.1025.41605 Docker Compose 部署指南

用官方 compose bundle 手工部署 Fluxer 2026.1025.41605 全栈 20 服务,含 .env 密钥生成、数据持久化、运维命令与常见问题实测。

FluxerFluxer即时通讯DockerDocker Compose自托管2026/10/25

Fluxer 2026.1025.41605 Docker Compose 部署指南

一、环境信息

本文演示环境为一台 Ubuntu 22.04 LTS 服务器,配置 4 vCPU / 8 GB 内存 / 40 GB SSD 磁盘;Docker Engine 29.1.3,Docker Compose v2.40.3;目标部署版本为 Fluxer 2026.1025.41605,访问地址 http://fluxer.test:8080。

1.1 演示环境配置表

项目配置
服务器Ubuntu 22.04 LTS(4 vCPU / 8 GB / 40 GB SSD)
Docker Engine29.1.3
Docker Composev2.40.3
软件版本Fluxer 2026.1025.41605(官方 compose bundle 2026.1025.41605)
部署目录/opt/fluxer
服务数量20 个
访问地址http://fluxer.test:8080(私有域名 .test,反向代理模式)

官方文档给出的生产建议为 2 vCPU / 4 GB / 40 GB 磁盘起步,语音频道建议 4 核;官方安装器实测预检为内存低于 2 GB、磁盘低于 20 GB 直接拒绝安装。20 个常驻服务的服务器内存低于 4 GB 时体验很差,请勿用 1C2G 小机器硬扛。

二、部署形态与镜像选型说明

Fluxer 官方只提供容器化交付,compose 编排是它的原生形态。官方发布渠道同时提供三种等价入口:

  1. rexctl 安装器:rexctl install 下载官方 bundle 并按交互/参数生成配置后启动编排,再叠加一个 rexctl start/stop/status 的 systemd 生命周期封装,适合单服务器快速上线(详见《Ubuntu 22.04 安装 Fluxer 2026.1025.41605 指南》)。
  2. 官方 compose bundle(本文方式):从发布资产直接下载 fluxer-compose-<版本>.tar.gz,手工解压、填写 .env、docker compose up。不引入 rexctl,部署单元完全由 Docker Compose 直管,适合已有 compose 运维体系(配置入库、CI 下发、统一监控)的团队。
  3. Helm Chart:源码仓 deploy/helm/ 附带,面向 Kubernetes 集群,不在本文范围。

2.1 镜像清单

整栈 20 个服务使用 19 个镜像,分两类:

  • Fluxer 自研服务镜像(12 个,ghcr.io/fluxerapp/fluxer-*,按 sha256 摘要锁定,不可变):api(api 与 worker 两个服务共用)、gateway、media-proxy、admin、push、static(static-proxy 服务)、app-proxy-self-hosted、snowflakes、users、gifs、messages、unfurl。
  • 第三方基础镜像(7 个,compose 中用 ${FLUXER_*_IMAGE:-默认} 参数化,可指向内部镜像仓库):postgres:16-alpine、valkey/valkey:9.1-alpine、nats:2.14-alpine、getmeili/meilisearch:v1.53、chrislusf/seaweedfs:4.47、livekit/livekit-server:v1.12.0、caddy:2.11-alpine。

自研镜像按摘要锁定意味着:升级 = 换新版 bundle 的 compose 文件,而不是改 tag。第三方镜像可用 FLUXER_POSTGRES_IMAGE、FLUXER_CADDY_IMAGE 等环境变量覆盖(离线/内网环境指向内部 registry)。

2.2 端口规划

端口用途说明
8080edge 对内监听(反代模式)宿主反代/隧道连进来的入口,由 .env 的 FLUXER_EDGE_BIND 决定
80 / 443 TCP+UDPedge 直接服务模式默认 compose 文件(Fluxer 自管 HTTPS 证书,h3 需要 443/udp)
7881 TCP / 7882 UDP语音(LiveKit RTC)compose 默认直接发布到宿主机

三、前置条件

  • Ubuntu 22.04(或其他主流 Linux 发行版)64 位;Docker ≥ 24.0(或 Podman ≥ 5.4)+ Compose v2;内存 ≥ 4 GB、磁盘 ≥ 20 GB。
  • 一个域名(公网 DNS 指向服务器,或内部域名/私有后缀 .test 用于测试)。
  • 能访问 ghcr.io 与 Docker Hub(拉取镜像);国内网络为宿主 Docker 配置代理。
  • 安装 curl、GNU tar。

四、部署步骤

4.1 安装 Docker Engine 与 Compose 插件

root@fluxer:~# apt-get update && apt-get install -y docker.io docker-compose-v2
root@fluxer:~# docker version && docker compose version
Client:
 Version:           29.1.3
 API version:       1.52
 Go version:        go1.26.0
 Git commit:        fbf34ed
 Built:             Fri Dec 12 21:21:44 2025
 OS/Arch:           linux/amd64
 Context:           default

Server: Docker Engine - Community
 Engine:
  Version:          29.1.3
  API version:      1.52 (minimum version 1.44)
  Go version:       go1.25.5
  Git commit:       fbf34ed
  Built:            Fri Dec 12 21:21:44 2025
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          v2.2.0
  GitCommit:        1c4457e00facac03ce1d75f7b6777a7a851e5c41
 runc:
  Version:          1.3.4
  GitCommit:        v1.3.4-0-gd6d73eb8
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0
Docker Compose version v2.40.3

如果你的环境拉取镜像需要走代理,为 dockerd 配置 systemd 代理后重启 Docker:

mkdir -p /etc/systemd/system/docker.service.d
cat > /etc/systemd/system/docker.service.d/proxy.conf <<'EOF'
[Service]
Environment="HTTP_PROXY=http://192.168.1.62:7897"
Environment="HTTPS_PROXY=http://192.168.1.62:7897"
Environment="NO_PROXY=localhost,127.0.0.1"
EOF
systemctl daemon-reload && systemctl restart docker

4.2 下载并解压官方 compose bundle

从 releases.fluxer.app 下载与目标版本对应的 bundle(文件名带构建号),解压到部署目录 /opt/fluxer:

root@fluxer:~# mkdir -p /opt/fluxer && cd /opt/fluxer
root@fluxer:/opt/fluxer# curl -fsSLO https://releases.fluxer.app/2026.1025.41605/fluxer-compose-2026.1025.41605.tar.gz
root@fluxer:/opt/fluxer# tar xzf fluxer-compose-2026.1025.41605.tar.gz && rm fluxer-compose-2026.1025.41605.tar.gz
root@fluxer:/opt/fluxer# ls -la
total 92
drwxr-xr-x 4 root root 4096 Oct 25 07:33 .
drwxr-xr-x 3 root root 4096 Oct 25 07:28 ..
-rw-r--r-- 1 root root  1247 Oct 25 07:33 .env
-rw-r--r-- 1 root root   534 Oct 25 04:24 .env.example
-rw-r--r-- 1 root root  1453 Oct 25 04:24 Caddyfile
drwxr-xr-x 2 root root 4096 Oct 25 04:24 caddy.d
drwxr-xr-x 2 root root 4096 Oct 25 04:24 certs
-rw-r--r-- 1 root root   265 Oct 25 04:24 docker-compose.proxy.yml
-rw-r--r-- 1 root root 45568 Oct 25 04:24 docker-compose.yml
-rw-r--r-- 1 root root   297 Oct 25 04:24 external-object-store.compose.yml
-rw-r--r-- 1 root root    42 Oct 25 04:24 ipv6.compose.yml
-rw-r--r-- 1 root root   259 Oct 25 04:24 tunnel.compose.yml

(上表中 .env 是下一步生成的,刚解压时不存在。)bundle 内容一览:

文件作用
docker-compose.yml主编排文件(20 个服务全定义,45 KB)
.env.example环境变量模板(所有密钥都在这里)
Caddyfileedge(Caddy)唯一配置文件,站点地址/TLS 等全部由环境变量驱动
caddy.d/自定义 Caddy 片段目录,挂载到 /etc/caddy/conf.d,被 Caddyfile import
certs/自定义证书目录,挂载到 /certs(配合 FLUXER_EDGE_TLS 使用内部证书)
docker-compose.proxy.yml覆盖文件:反向代理模式(edge 只监听 FLUXER_EDGE_BIND,不办证书)
tunnel.compose.yml覆盖文件:隧道模式(cloudflared 类隧道回源到 80)
ipv6.compose.yml覆盖文件:为 stack 网络启用 IPv6
external-object-store.compose.yml覆盖文件:停用内置 SeaweedFS,改用外部 S3 兼容对象存储

4.3 编写 .env(全部密钥一次生成)

.env.example 列出了必须填写的全部变量——1 个开关、2 个实例参数(域名 + 首次设置令牌)、5 个组件口令,以及 12 个应用级密钥:

root@fluxer:/opt/fluxer# cat .env.example
FLUXER_SETTINGS_LOADED=true
FLUXER_DOMAIN=
FLUXER_SETUP_TOKEN=
FLUXER_S3_ACCESS_KEY=fluxer
LIVEKIT_API_KEY=fluxer
POSTGRES_PASSWORD=
MEILI_MASTER_KEY=
FLUXER_S3_SECRET_KEY=
FLUXER_SUDO_MODE_SECRET=
FLUXER_CONNECTION_INITIATION_SECRET=
FLUXER_PROFILE_PSEUDONYM_SECRET=
FLUXER_GATEWAY_RPC_AUTH_TOKEN=
FLUXER_ERLANG_COOKIE=
FLUXER_MEDIA_PROXY_SECRET_KEY=
FLUXER_ADMIN_SECRET_KEY_BASE=
FLUXER_ADMIN_OAUTH_CLIENT_SECRET=
LIVEKIT_API_SECRET=
FLUXER_MEDIA_PROXY_UPLOAD_RELAY_SECRET_BASE64=
FLUXER_VAPID_PUBLIC_KEY=
FLUXER_VAPID_PRIVATE_KEY=

除了 FLUXER_SETUP_TOKEN(32 字符,浏览器首次设置用的一次性令牌)外,十六进制密钥都可以用 OpenSSL 生成;FLUXER_MEDIA_PROXY_UPLOAD_RELAY_SECRET_BASE64 是 base64;VAPID 密钥对(Web Push)必须用 ECDSA P-256 算法生成,不能用随机串。一条命令生成整套密钥并写入 .env(本文用反代模式,绑定 0.0.0.0:8080,域名用私有后缀 fluxer.test):

root@fluxer:/opt/fluxer# python3 - <<'EOF'
import secrets, base64
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat

def h(n=32): return secrets.token_hex(n)
def b64url(b): return base64.urlsafe_b64encode(b).rstrip(b"=").decode()

key = ec.generate_private_key(ec.SECP256R1())
priv = key.private_numbers().private_value.to_bytes(32, "big")
pub = key.public_key().public_bytes(Encoding.X962, PublicFormat.UncompressedPoint)

env = f"""FLUXER_SETTINGS_LOADED=true
FLUXER_DOMAIN=fluxer.test
FLUXER_SETUP_TOKEN={secrets.token_hex(16)}
FLUXER_S3_ACCESS_KEY=fluxer
LIVEKIT_API_KEY=fluxer
POSTGRES_PASSWORD={h()}
MEILI_MASTER_KEY={h()}
FLUXER_S3_SECRET_KEY={h()}
FLUXER_SUDO_MODE_SECRET={h()}
FLUXER_CONNECTION_INITIATION_SECRET={h()}
FLUXER_PROFILE_PSEUDONYM_SECRET={h()}
FLUXER_GATEWAY_RPC_AUTH_TOKEN={h()}
FLUXER_ERLANG_COOKIE={h()}
FLUXER_MEDIA_PROXY_SECRET_KEY={h()}
FLUXER_ADMIN_SECRET_KEY_BASE={h(64)}
FLUXER_ADMIN_OAUTH_CLIENT_SECRET={h()}
LIVEKIT_API_SECRET={h()}
FLUXER_MEDIA_PROXY_UPLOAD_RELAY_SECRET_BASE64={base64.b64encode(secrets.token_bytes(32)).decode()}
FLUXER_VAPID_PUBLIC_KEY={b64url(pub)}
FLUXER_VAPID_PRIVATE_KEY={b64url(priv)}
FLUXER_PUBLIC_SCHEME=http
FLUXER_PUBLIC_PORT=8080
FLUXER_EDGE_BIND=0.0.0.0:8080
"""
open(".env", "w").write(env)
print("ok")
EOF
ok
root@fluxer:/opt/fluxer# chmod 600 .env && cat .env
FLUXER_SETTINGS_LOADED=true
FLUXER_DOMAIN=fluxer.test
FLUXER_SETUP_TOKEN=ce4e0b00850a218cb09a2a4ec4f884d6
FLUXER_S3_ACCESS_KEY=fluxer
LIVEKIT_API_KEY=fluxer
POSTGRES_PASSWORD=1e3cb7e45ed9d087da7a809f0c3bf45e
MEILI_MASTER_KEY=7269f247d87958bee1ed20bb1003e933
FLUXER_S3_SECRET_KEY=0113babb9cc7baea2e3b4a9a0e9d309a
FLUXER_SUDO_MODE_SECRET=616b62fdcde50f89113112524d00d0f8
FLUXER_CONNECTION_INITIATION_SECRET=aba59fc8df0aeb552a575fd72e981824
FLUXER_PROFILE_PSEUDONYM_SECRET=c652c196b47dbb7a52522b843077aa8b
FLUXER_GATEWAY_RPC_AUTH_TOKEN=4e5a77ab74c45f4dee80930847aaae2e
FLUXER_ERLANG_COOKIE=8a67af7a8d1c955caca3e3bb26c74de8
FLUXER_MEDIA_PROXY_SECRET_KEY=df74bc35e3fee7659346acda56452aa6
FLUXER_ADMIN_SECRET_KEY_BASE=e2b17afc99ab8aacefd7d4b9331e7eecca409caa943d2285cbab4ce7d4fe61bc
FLUXER_ADMIN_OAUTH_CLIENT_SECRET=366badbf95e63bb6d8da4a31b40a965b
LIVEKIT_API_SECRET=0d6669e686faf1839f9f25641453864e
FLUXER_MEDIA_PROXY_UPLOAD_RELAY_SECRET_BASE64=vGXQjI5Iz0YCWB2iQy7uUIOxMpNZtBZJHWaELFr5RM0=
FLUXER_VAPID_PUBLIC_KEY=BIpjEgHwBmNpMSSAnWH-7wxcApZfd5i7QTd5rSSoPBeoi7Wh2S0G38HQI1uF2L9dgQZ-WfXthxZ1p0P0z_b_zLk
FLUXER_VAPID_PRIVATE_KEY=X32Ol8wEo26352aI_M2UonKTgIV8NpYRy_urHLr8w6Y
FLUXER_PUBLIC_SCHEME=http
FLUXER_PUBLIC_PORT=8080
FLUXER_EDGE_BIND=0.0.0.0:8080

三个补充变量不在 .env.example 里,但反代模式下很常用:FLUXER_PUBLIC_SCHEME / FLUXER_PUBLIC_PORT 告诉实例「用户最终看到的地址 scheme 与端口」(生成邀请链接、发现端点都靠它);FLUXER_EDGE_BIND 是 docker-compose.proxy.yml 里 edge 发布端口用的宿主绑定地址(默认 127.0.0.1:8080,即只接受本机反代回源)。

FLUXER_VAPID_PUBLIC_KEY / FLUXER_VAPID_PRIVATE_KEY 不能留空也不能用随机字符串:这两个是 Web Push 的 VAPID 密钥对,必须是 ECDSA P-256(prime256v1)曲线生成的 base64url 编码密钥,留空会导致 push 服务异常。上面的 Python 片段用 cryptography 库生成,与官方安装器内部生成方式等价。

4.4 拉取镜像并启动整栈

先拉取镜像(首次约 20 个镜像、1.9 GB,需要几分钟;以下回显为镜像已在本地缓存时的重复执行结果,秒级完成):

root@fluxer:/opt/fluxer# docker compose -p fluxer-20261025-stack -f docker-compose.yml -f docker-compose.proxy.yml --env-file .env pull
 meilisearch Pulling
 edge Pulling
 seaweedfs Pulling
 unfurl Pulling
 livekit Pulling
 app-proxy Pulling
 unfurl Pulled
 gateway Pulled
 users Pulled
 gifs Pulled
 static-proxy Pulled
 snowflakes Pulled
 admin Pulled
 api Pulled
 push Pulled
 media-proxy Pulled
 messages Pulled
 app-proxy Pulled
 valkey Pulled
 nats Pulled
 postgres Pulled
 meilisearch Pulled
 seaweedfs Pulled
 livekit Pulled
 edge Pulled

启动(-p 指定项目名,避免与其他 compose 栈混淆;-f 叠加反代模式覆盖文件):

root@fluxer:/opt/fluxer# docker compose -p fluxer-20261025-stack -f docker-compose.yml -f docker-compose.proxy.yml --env-file .env up -d

启动过程有严格的依赖编排:postgres / valkey / nats / meilisearch / seaweedfs-init 先 healthy,api、worker、gateway 等应用服务才启动,edge 最后起。整栈 ready 后再次执行 up -d 的回显如下(所有服务已在运行):

 Container fluxer-20261025-stack-nats-1  Healthy
 Container fluxer-20261025-stack-api-1  Healthy
 Container fluxer-20261025-stack-gateway-1  Healthy
 Container fluxer-20261025-stack-edge-1  Waiting
 Container fluxer-20261025-stack-edge-1  Healthy

4.5 验证运行状态

root@fluxer:/opt/fluxer# docker compose -p fluxer-20261025-stack -f docker-compose.yml -f docker-compose.proxy.yml --env-file .env ps --format "table {{.Name}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}"
NAME                                   IMAGE                                                                                                                    STATUS                    PORTS
fluxer-20261025-stack-admin-1          ghcr.io/fluxerapp/fluxer-admin@sha256:d50cc677050e51bd0e68eeb7eb5d9cc352786dfe4921de50207be348fa7e299a                   Up 18 minutes (healthy)   8080/tcp
fluxer-20261025-stack-api-1            ghcr.io/fluxerapp/fluxer-api@sha256:8b34480e0c085c6b5873dc6c16989d60f4b94cad713b9ead7714a400ea40d544                     Up 18 minutes (healthy)   8080/tcp
fluxer-20261025-stack-app-proxy-1      ghcr.io/fluxerapp/fluxer-app-proxy-self-hosted@sha256:984b58c9676822d58858958bc60410156b589324698d13af02c65624d174d8cc   Up 18 minutes (healthy)   8080/tcp
fluxer-20261025-stack-edge-1           caddy:2.11-alpine                                                                                                        Up 18 minutes (healthy)   80/tcp, 443/tcp, 2019/tcp, 443/udp, 0.0.0.0:8080->8080/tcp
fluxer-20261025-stack-gateway-1        ghcr.io/fluxerapp/fluxer-gateway@sha256:7da9bbf12263132973281855cef7b7e1ce40623784e92dea17ffe7e32b709562                 Up 18 minutes (healthy)   4369/tcp, 8080-8081/tcp
fluxer-20261025-stack-gifs-1           ghcr.io/fluxerapp/fluxer-gifs@sha256:1b90610660264f548cd835d62c9a400806f5491b7a02ecf3ef189702da355427                    Up 18 minutes (healthy)   8090/tcp
fluxer-20261025-stack-livekit-1        livekit/livekit-server:v1.12.0                                                                                           Up 19 minutes (healthy)   0.0.0.0:7881->7881/tcp, [::]:7881->7881/tcp, 0.0.0.0:7882->7882/udp, [::]:7882->7882/udp
fluxer-20261025-stack-media-proxy-1    ghcr.io/fluxerapp/fluxer-media-proxy@sha256:5718fac76560b380934252f7b1525d41ea4120262d57a861c7f80370a1c3b94d             Up 18 minutes (healthy)   8080/tcp
fluxer-20261025-stack-meilisearch-1    getmeili/meilisearch:v1.53                                                                                               Up 19 minutes (healthy)   7700/tcp
fluxer-20261025-stack-messages-1       ghcr.io/fluxerapp/fluxer-messages@sha256:92ccae001d54914637de4d1d4431e5860ecf7869ec028de0afbbca5f1d5cedb8                Up 18 minutes (healthy)   8090/tcp
fluxer-20261025-stack-nats-1           nats:2.14-alpine                                                                                                         Up 19 minutes (healthy)   4222/tcp, 6222/tcp, 8222/tcp
fluxer-20261025-stack-postgres-1       postgres:16-alpine                                                                                                       Up 19 minutes (healthy)   5432/tcp
fluxer-20261025-stack-push-1           ghcr.io/fluxerapp/fluxer-push@sha256:1f435ffbc08d7e9ee4bc2585dbc412820ea44f1ea4b319af7df1f3ba19dadf56                    Up 18 minutes (healthy)   8126-8127/tcp
fluxer-20261025-stack-seaweedfs-1      chrislusf/seaweedfs:4.47                                                                                                 Up 19 minutes (healthy)   7333/tcp, 8080/tcp, 8333/tcp, 8888/tcp, 9333/tcp, 18080/tcp, 18888/tcp, 19333/tcp
fluxer-20261025-stack-snowflakes-1     ghcr.io/fluxerapp/fluxer-snowflakes@sha256:53593ff4709df185678c9d54bd3d4e039943a0c7bf8d26019822a2517b472376              Up 18 minutes (healthy)   8090/tcp
fluxer-20261025-stack-static-proxy-1   ghcr.io/fluxerapp/fluxer-static@sha256:568d0232fd4779934c0cfe92edaafedbbcc43a44398ec7d84a741f07f3dd9e9c                  Up 19 minutes (healthy)   8080/tcp
fluxer-20261025-stack-unfurl-1         ghcr.io/fluxerapp/fluxer-unfurl@sha256:06295aeed77f015912523b9a3337813127108451cad59862cfb1d91a1cd47683                  Up 18 minutes (healthy)   8090/tcp
fluxer-20261025-stack-users-1          ghcr.io/fluxerapp/fluxer-users@sha256:3df3edaa475bce10f197e43c6ffcc5e09f7417bbe8c510d28bf0da487f16a071                   Up 18 minutes (healthy)   8090/tcp
fluxer-20261025-stack-valkey-1         valkey/valkey:9.1-alpine                                                                                                 Up 19 minutes (healthy)   6379/tcp
fluxer-20261025-stack-worker-1         ghcr.io/fluxerapp/fluxer-api@sha256:8b34480e0c085c6b5873dc6c16989d60f4b94cad713b9ead7714a400ea40d544                     Up 18 minutes (healthy)   8080/tcp

20 个服务全部 healthy。验证 HTTP 入口与 edge 健康检查:

root@fluxer:/opt/fluxer# curl -s -o /dev/null -w "%{http_code}\n" http://fluxer.test:8080/
root@fluxer:/opt/fluxer# curl -s -o /dev/null -w "%{http_code}\n" http://fluxer.test:8080/_health
200
200

再验证实例发现端点(Fluxer 客户端用它发现实例能力与各服务地址):

root@fluxer:/opt/fluxer# curl -s http://fluxer.test:8080/.well-known/fluxer
{"api_code_version":1,"endpoints":{"api":"http://fluxer.test:8080/api","api_client":"http://fluxer.test:8080/api","api_public":"http://fluxer.test:8080/api","gateway":"ws://fluxer.test:8080/gateway","media":"http://fluxer.test:8080/media","upload_relay":"http://fluxer.test:8080/media","static_cdn":"http://fluxer.test:8080","marketing":"http://fluxer.test:8080","admin":"http://fluxer.test:8080/admin","invite":"http://fluxer.test:8080/invite","gift":"http://fluxer.test:8080/gift","webapp":"http://fluxer.test:8080"},"captcha":{"provider":"altcha"},"features":{"voice_enabled":true,"stripe_enabled":false,"premium_enabled":false,"stripe_serviceable":false,"self_hosted":true,"presigned_attachment_uploads":true,"emails_enabled":false,"phone_verification_enabled":false,"discovery_enabled":true,"account_identity":"username","tag_style":"none","identifier_first":false,"instant_accounts":false,"email_codes":false,"date_of_birth_step":true,"instant_account_availability":"disabled","interaction_tier":"enforced","guild_join_gate":"always","email_delivery_state":"off_username_mode","verification_basis":"local_credential"},"gif":{"provider":"klipy","display_name":"Klipy","attribution_required":false},"sso":{"enabled":false,"enforced":false,"display_name":null,"button_label":null,"redirect_uri":"http://fluxer.test:8080/auth/sso/callback"},"registration":{"mode":"open","admin_registration_urls_enabled":true},"community":{"single_community":false,"direct_messages_disabled":false,"friends_disabled":false,"guild_create_access":true},"services":{"gif_enabled":false,"youtube_enabled":false,"bluesky_enabled":false},"limits":{"version":2,"traitDefinitions":[],"rules":[{"id":"default","overrides":{"feature_animated_avatar":1,"feature_animated_banner":1,"feature_custom_discriminator":1,"feature_early_access":1,"feature_global_expressions":1,"feature_higher_video_quality":1,"feature_per_guild_profiles":1,"feature_voice_entrance_sounds":1,"max_bookmarks":300,"max_custom_backgrounds":15,"max_favorite_memes":500,"max_guilds":200,"max_attachment_file_size":524288000,"max_message_length":4000,"max_guild_emojis":500,"max_guild_stickers":500}},{"id":"very_large_guild","filters":{"guildFeatures":["VERY_LARGE_GUILD"]},"overrides":{"max_guild_members":10000000}}],"defaultsHash":"7b8p7j"},"push":{"public_vapid_key":"BIpjEgHwBmNpMSSAnWH-7wxcApZfd5i7QTd5rSSoPBeoi7Wh2S0G38HQI1uF2L9dgQZ-WfXthxZ1p0P0z_b_zLk"},"app_public":{"branding":{"product_name":"Fluxer","icon_url":null,"symbol_url":null,"logo_url":null,"wordmark_url":null,"favicon_url":null,"favicon_dark_url":null,"theme_color":null,"status_page_url":null,"status_page_incident_history_url":null,"premium_product_name":"Premium","premium_info_url":null,"sign_in_backdrop_url":null,"custom_css":null,"upload_limit_message":null,"trusted_domains":[]},"setup":{"configured":false,"admin_url":"http://fluxer.test:8080/admin","account_identity_locked":false,"account_identity_lock_reason":null,"setup_token_required":true},"legal":{"terms_url":null,"privacy_url":null,"guidelines_url":null},"registration":{"collect_date_of_birth":false}},"notice":null,"domain_migration":{"enabled":false,"anonymous_rollout_basis_points":0,"rollout_salt":"domain-migration-v1","standalone_forwarding":false}}

返回 200 且 JSON 中的 endpoints 全部指向你的域名,说明整栈已就绪。注意其中 setup.configured: false 与 setup_token_required: true——实例还在等待首次设置。

4.6 首次浏览器设置

浏览器打开 http://fluxer.test:8080,进入首次设置向导,粘贴 .env 里的 FLUXER_SETUP_TOKEN,按提示创建管理员账号(演示账号:root / q1w2e3r4)、填写实例名称。完成后发现端点中的 setup.configured 变为 true,实例进入正常使用状态。

如果丢了 setup token,直接 grep FLUXER_SETUP_TOKEN /opt/fluxer/.env 找回即可。设置完成后它可以留在 .env 里,不再生效。

五、数据持久化

compose 文件定义了 8 个命名卷(实际名字带项目名前缀 fluxer-20261025-stack_):

卷名对应服务内容可重建
postgres-datapostgresPostgreSQL 数据目录(账号、频道、消息元数据)否,必须备份
seaweedfs-dataseaweedfs对象存储数据(头像、附件、媒体文件)否,必须备份
nats-datanatsJetStream 队列数据(待投递任务)基本可重建,停机时建议备份
valkey-datavalkey缓存(RDB 持久化)是
meilisearch-datameilisearch搜索索引是(重建耗时)
edge-dataedgeCaddy 证书与状态反代模式下基本为空
edge-configedgeCaddy 自动配置是
media-proxy-tmpmedia-proxy媒体处理临时文件是

备份要点:真正不可重建的是 postgres-data 与 seaweedfs-data。最简单可靠的方式是停机冷备:

cd /opt/fluxer
docker compose -p fluxer-20261025-stack -f docker-compose.yml -f docker-compose.proxy.yml --env-file .env stop
tar czf fluxer-backup-$(date +%F).tar.gz -C /var/lib/docker/volumes \
  fluxer-20261025-stack_postgres-data fluxer-20261025-stack_seaweedfs-data
docker compose -p fluxer-20261025-stack -f docker-compose.yml -f docker-compose.proxy.yml --env-file .env start

.env 文件(含全部密钥)必须随备份一起保存,否则恢复后会话签名与推送凭据全部失效。

六、环境变量表

.env.example 中必须提供的变量如下(compose 文件里还有大量 ${FLUXER_*:-默认} 形式的可选调优变量,如各服务内存上限 FLUXER_API_MEMORY_LIMIT、worker 副本数 FLUXER_WORKER_REPLICAS、第三方镜像覆盖 FLUXER_*_IMAGE 等,见 bundle 内注释):

变量必填示例说明
FLUXER_SETTINGS_LOADED是true置 true 表示「设置已加载」,跳过引导检查
FLUXER_DOMAIN是fluxer.test实例域名
FLUXER_SETUP_TOKEN是32 字符 hex首次浏览器设置的令牌
POSTGRES_PASSWORD是32 字符 hexPostgreSQL 口令
MEILI_MASTER_KEY是32 字符 hexMeilisearch 主密钥
FLUXER_S3_ACCESS_KEY / FLUXER_S3_SECRET_KEY是fluxer / 32 字符 hex内置 SeaweedFS 的 S3 凭据
FLUXER_SUDO_MODE_SECRET是32 字符 hex管理员 sudo 模式会话签名
FLUXER_CONNECTION_INITIATION_SECRET是32 字符 hex连接初始化签名
FLUXER_PROFILE_PSEUDONYM_SECRET是32 字符 hex用户资料假名化密钥
FLUXER_GATEWAY_RPC_AUTH_TOKEN是32 字符 hex网关内部 RPC 令牌
FLUXER_ERLANG_COOKIE是32 字符 hexErlang 分布式 cookie(gateway 与 Rust 服务间)
FLUXER_MEDIA_PROXY_SECRET_KEY是32 字符 hex媒体代理签名密钥
FLUXER_ADMIN_SECRET_KEY_BASE是64 字符 hex管理面板会话密钥
FLUXER_ADMIN_OAUTH_CLIENT_SECRET是32 字符 hex管理面板 OAuth 客户端密钥
LIVEKIT_API_KEY / LIVEKIT_API_SECRET是fluxer / 32 字符 hexLiveKit API 凭据
FLUXER_MEDIA_PROXY_UPLOAD_RELAY_SECRET_BASE64是base64媒体上传中继密钥(base64 编码)
FLUXER_VAPID_PUBLIC_KEY / FLUXER_VAPID_PRIVATE_KEY是base64urlWeb Push VAPID 密钥对(必须 ECDSA P-256 生成)
FLUXER_PUBLIC_SCHEME反代模式建议http用户访问地址的 scheme
FLUXER_PUBLIC_PORT反代模式建议8080用户访问地址的端口
FLUXER_EDGE_BIND反代模式建议0.0.0.0:8080edge 发布到宿主机的绑定地址(proxy/tunnel 覆盖文件使用,默认 127.0.0.1:8080)

密钥轮换:修改 .env 中任意密钥后 docker compose ... up -d 即会重建相关服务。注意 POSTGRES_PASSWORD、MEILI_MASTER_KEY 这类数据组件口令一旦初始化完成再改会连不上已有数据(口令已固化在数据卷里),需要同步改组件内的口令或重建数据卷。

七、配置参数说明

compose 形态下有两层配置:

  1. 部署层(本文 .env + compose 文件):域名、监听地址、密钥、镜像、资源限额。改这些需要 up -d 重建对应服务。
  2. 实例行为层(注册策略、品牌、登录方式、清理策略等 170+ 项):首次设置完成后存于数据库,通过管理面板(http://fluxer.test:8080/admin)调整,多数即时生效、无需重启。发现端点 .well-known/fluxer 返回的 JSON(见 4.5)就是这些行为参数的对外投影。

实例行为层各项参数的详解见《Fluxer 2026.1025.41605 配置参数详解》;rexctl 的手工编排交还与接管用法见《Fluxer 2026.1025.41605 常用 CLI 命令与工具详解》。

八、配置文件挂载

edge(Caddy)是挂载本地配置最多的服务,compose 中的挂载如下:

    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - ./certs:/certs:ro
      - ./caddy.d:/etc/caddy/conf.d:ro
      - edge-data:/data
      - edge-config:/config

Caddyfile 是主配置,站点地址、TLS 指令、信任代理全部用环境变量占位,三种模式(直接服务/反代/隧道)的差异由 compose 覆盖文件注入不同的 FLUXER_EDGE_SITE_ADDRESS、FLUXER_EDGE_TLS_DIRECTIVE 实现,而不是换 Caddyfile:

root@fluxer:/opt/fluxer# cat Caddyfile
{
	servers {
		trusted_proxies static {$FLUXER_EDGE_TRUSTED_PROXIES:private_ranges}
		trusted_proxies_strict
		protocols {$FLUXER_EDGE_PROTOCOLS:h1 h2 h3}
	}
	{$FLUXER_EDGE_GLOBAL_OPTIONS}
}

(upstream) {
	reverse_proxy {args[0]} {
		header_up X-Forwarded-For {client_ip}
		header_up X-Real-IP {client_ip}
		header_up -Forwarded
	}
}

{$FLUXER_EDGE_SITE_ADDRESS} {
	{$FLUXER_EDGE_TLS_DIRECTIVE}
	import /etc/caddy/conf.d/*.caddy
	encode {$FLUXER_EDGE_ENCODE:zstd gzip}

	handle /_health {
		respond "OK" 200
	}

	handle /_edge {
		header Cache-Control no-store
		respond "{system.hostname}" 200
	}

	handle_path /api/* {
		import upstream api:8080
	}

	handle /gateway {
		rewrite * /
		import upstream gateway:8080
	}

	handle_path /gateway/* {
		import upstream gateway:8080
	}

	handle_path /media/* {
		import upstream media-proxy:8080
	}

	handle_path /livekit/* {
		import upstream livekit:7880
	}

	handle {$FLUXER_ADMIN_BASE_PATH:/admin} {
		rewrite * /
		import upstream admin:8080
	}

	handle_path {$FLUXER_ADMIN_BASE_PATH:/admin}/* {
		import upstream admin:8080
	}

	@staticAssets path /web/* /emoji/* /libs/* /avatars/* /badges/* /desktop/*
	handle @staticAssets {
		import upstream static-proxy:8080
	}

	handle /.well-known/fluxer {
		import upstream api:8080
	}

	handle {
		import upstream app-proxy:8080
	}
}

:8088 {
	handle /.well-known/fluxer {
		import upstream api:8080
	}

	handle /_edge {
		respond "{system.hostname}" 200
	}
}

如需自定义路由(限流、额外请求头、IP 白名单),把 .caddy 片段放进 caddy.d/ 即可被 import /etc/caddy/conf.d/*.caddy 收进去,不必改主 Caddyfile。改完重启 edge:

docker compose -p fluxer-20261025-stack -f docker-compose.yml -f docker-compose.proxy.yml --env-file .env restart edge

九、等价 docker run 示例(了解用)

compose 文件是唯一定义来源,不建议手工 docker run。以下为帮助理解单个服务的等价写法(以 api 服务为例,实际请用 compose 管理):

docker run -d \
  --name fluxer-api \
  --network fluxer-20261025-stack_fluxer \
  --env-file /opt/fluxer/.env \
  -e FLUXER_API_PORT=8080 \
  --restart unless-stopped \
  ghcr.io/fluxerapp/fluxer-api@sha256:8b34480e0c085c6b5873dc6c16989d60f4b94cad713b9ead7714a400ea40d544

注意 20 个服务之间存在严格的启动依赖与健康检查编排(postgres/valkey/nats/meilisearch 必须先 healthy、seaweedfs-init 必须跑完,api/worker/gateway 才会启动),手工维护这些依赖得不偿失——这也是官方只发布 compose/helm 两种形态的原因。

十、常用运维命令

以下命令都在 /opt/fluxer 下执行,为节省篇幅把项目名与文件参数定义为 shell 别名:

root@fluxer:~# cd /opt/fluxer
root@fluxer:/opt/fluxer# alias dc='docker compose -p fluxer-20261025-stack -f docker-compose.yml -f docker-compose.proxy.yml --env-file .env'

查看状态:dc ps(输出见 4.5)。查看某个服务的日志(以 api 为例):

root@fluxer:/opt/fluxer# dc logs --tail 4 api
api-1  | {"level":"info","time":"2026-10-25T07:54:16.675Z","service":"fluxer-api","env":"production","method":"POST","path":"/internal/rpc","status":200,"durationMs":1,"msg":"Request completed"}
api-1  | {"level":"info","time":"2026-10-25T07:54:27.800Z","service":"fluxer-api","env":"production","method":"POST","path":"/internal/rpc","status":200,"durationMs":0,"msg":"Request completed"}
api-1  | {"level":"info","time":"2026-10-25T07:54:30.724Z","service":"fluxer-api","env":"production","method":"GET","path":"/.well-known/fluxer","status":200,"durationMs":11,"msg":"Request completed"}
api-1  | {"level":"info","time":"2026-10-25T07:54:30.727Z","service":"fluxer-api","env":"production","method":"GET","path":"/.well-known/fluxer","status":200,"durationMs":9,"msg":"Request completed"}

重启单个服务 / 整栈:

dc restart api        # 重启单个服务
dc restart            # 整栈滚动重启
dc stop && dc start   # 整栈停止再启动(不删容器)

修改 .env 或 caddy.d 片段后使生效:

dc up -d              # 自动重建受影响的服务

查看资源占用(compose 文件为每个服务预设了内存上限,如 api 2560mb、edge 384mb,可用 FLUXER_*_MEMORY_LIMIT 调整):

root@fluxer:/opt/fluxer# docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}" | head -10
NAME                                   CPU %     MEM USAGE / LIMIT
fluxer-20261025-stack-app-proxy-1      0.00%     3.844MiB / 256MiB
fluxer-20261025-stack-edge-1           0.00%     21.15MiB / 384MiB
fluxer-20261025-stack-push-1           0.00%     1.918MiB / 256MiB
fluxer-20261025-stack-admin-1          0.00%     10.21MiB / 256MiB
fluxer-20261025-stack-api-1            0.01%     166.8MiB / 2.5GiB
fluxer-20261025-stack-worker-1         0.01%     143.1MiB / 2.5GiB
fluxer-20261025-stack-media-proxy-1    0.00%     34.78MiB / 1GiB
fluxer-20261025-stack-users-1          0.01%     1.41MiB / 256MiB
fluxer-20261025-stack-messages-1       0.01%     3.066MiB / 256MiB

升级到新构建号:自研镜像按 sha256 摘要锁定,升级方式是下载新版 bundle,用新版 docker-compose.yml 替换旧文件(.env 沿用),然后:

dc pull && dc up -d

整栈服务会按新摘要重建,数据库迁移由 api 启动时自动执行。升级前务必备份(见第五节)。

十一、常见问题

Q1:docker compose pull 时某镜像拉取失败,报超时或 failed to resolve reference?

Docker Hub 与 ghcr.io 连通性问题。先确认 dockerd 代理已配置(见 4.1),再单独重试该镜像。内网环境用 FLUXER_POSTGRES_IMAGE 等变量把第三方镜像指向内部 registry,自研镜像批量搬运后修改 compose 文件中的 image 字段。

Q2:启动后 api 一直 unhealthy,日志报数据库连接失败?

.env 中 POSTGRES_PASSWORD 在首次初始化后被改动过。PostgreSQL 的口令已固化在 postgres-data 卷里,.env 改了但卷里没改。两种解法:把 .env 口令改回初始值;或者进数据库改口令:

dc exec postgres psql -U postgres -c "ALTER USER postgres PASSWORD '新口令';"

Q3:浏览器打开提示「Invalid setup token」或一直停在设置页?

粘贴的 FLUXER_SETUP_TOKEN 与 .env 不一致(多空格/换了行),用 grep FLUXER_SETUP_TOKEN .env 精确复制。若发现端点显示 setup.configured: true,说明设置已完成过,设置页会关闭,这是正常现象。

Q4:推送通知不工作,客户端收不到 Web Push?

FLUXER_VAPID_PUBLIC_KEY / FLUXER_VAPID_PRIVATE_KEY 未按 ECDSA P-256 生成或留空。按 4.3 的 Python 片段重新生成密钥对,dc up -d 重建 push 服务。

Q5:dc ps 显示 healthy 但访问 8080 超时?

检查 FLUXER_EDGE_BIND:proxy 覆盖文件默认绑定 127.0.0.1:8080,只有本机能访问;要让局域网/反代服务器从外部连进来,需显式设 FLUXER_EDGE_BIND=0.0.0.0:8080 并 dc up -d。另外确认宿主防火墙放行了该端口。

Q6:想换域名怎么办?

改 .env 的 FLUXER_DOMAIN(反代模式连同 FLUXER_PUBLIC_SCHEME/FLUXER_PUBLIC_PORT),dc up -d。实例行为层的域名迁移(domain_migration.* 参数,灰度切换)见《Fluxer 2026.1025.41605 配置参数详解》。

Q7:磁盘占用增长快,主要是哪个卷?

seaweedfs-data(附件/媒体对象)与 postgres-data(消息)。用 docker system df -v | grep fluxer 看各卷大小;附件衰减清理策略(media.attachment_decay.*)见《Fluxer 2026.1025.41605 配置参数详解》第五节。

十二、参考链接