这是使用 Docker Compose 工具来安装和运行 Misskey(一个开源的去中心化社交平台)的标准化操作流程。

很久前就一直想弄个二次元氛围爆满的网站,博客也是用动漫主题,奈何使用起来各种体验并不太满意,毕竟都不是原生的二次元风,唯一了解到的开源免费套件就是去中心化的社交平台 Misskey ,一个充满二次元味道的微博网站,正巧我也一直很想弄个微博网站,WordPress 发短文还是感觉差点意思,无论是排版方面,还是发布方面都不太原生,发个说说都像是发文章那样。

不过 Misskey 需要的配置还挺高的,不是一般 VPS 能承受得起的,以我的经验部署完后占用 1.5G 内存左右,也就是说至少要 2G 内存的主机才够用,所以我一直没弄,直到看到吃灰许久的迷你主机……于是我把迷你主机装上 Debian 无桌面环境版本,部署上 Misskey ,通过内网穿透的方式使用,总算是发挥了一丢丢作用(功耗低7x24小时开着应该没啥问题→_→),部署过程中发现网上教程要不就太复杂,要不就是不够详细,故此将自己的部署过程以及遇到的一些问题记录下来。

部署完成效果确实挺二次元的,如图:

核心警告:请务必注意!

对于一个已经投入使用的网站,绝对不要重新执行“初始化数据库”的步骤!

这样做会清空你所有的数据,包括用户、帖子、文件等。初始化是且仅是第一次安装时才需要执行的操作。

第一步:准备工作 (在 Debian 系统上安装 Docker Engine)

在开始之前,请确保您的服务器已经安装好了 Docker 和 Docker Compose 这两个核心软件。本指南的所有操作都依赖于它们。

如果你连 Debian 系统都还没有,需要先进行安装,这个安装过程应该没有不会的吧?如果有,我再补充。

简单来说就是到 Debian 官网下载一个类似“debian-12.11.0-amd64-DVD-1.iso”的安装镜像,放到写入了引导的U盘里面,然后开机启动后安装提示一步步安装即可。因为我是在无网络环境下安装的,所以用了这个 DVD 版本——我的情况比较特殊,在方便接显示器的地方没有网线,在放服务器主机的地方有网络但没有显示器……

在无网络的情况下安装无显示器的服务器,建议先在有条件的环境下,配置好静态或动态IP地址。这样,当将服务器移至最终位置并接通网线后,它就可以使用指定的IP地址稳定地连接到网络,方便之后通过SSH等方式进行远程管理,但我在安装过程中不懂直接就跳过了网络配置。

补救方法是在有显示器的条件下:

1、识别网络接口名称:

首先,你需要找到你的网络接口名称。通过以下命令可以列出所有的网络接口:

ip a

或者

ls /sys/class/net/

常见的接口名称有 eth0ens33enp0s3 等。请记下您要配置的那个接口的名称。

2、编辑配置文件:

使用文本编辑器(如 nano 或 vim)打开网络配置文件 /etc/network/interfaces

sudo nano /etc/network/interfaces

3、配置网络:

动态IP (DHCP): 如果您的网络环境支持DHCP自动分配IP地址,添加以下内容:

auto <接口名>
allow-hotplug <接口名>
iface <接口名> inet dhcp

请将 <接口名> 替换为您在上一步中找到的实际接口名称。

静态IP: 对于服务器,更推荐使用静态IP。添加如下配置,并根据您的实际网络情况修改IP地址、子网掩码和网关:

auto <接口名>
iface <接口名> inet static
    address 192.168.1.100
    netmask 255.255.255.0
    gateway 192.168.1.1
    dns-nameservers 8.8.8.8 114.114.114.114

请将 <接口名> 以及IP地址、子网掩码、网关和DNS服务器地址替换为您的实际信息。

4、重启网络服务:

保存文件后,重启网络服务以使配置生效:

sudo systemctl restart networking.service

例如,我在使用 ip a 命令中看到 lo 和 enp1s0 分别是:

  • lo: 这是环回接口 (Loopback Interface),是每台Linux机器上都有的虚拟网络接口,用于本机进程间的通信。它的IP地址通常是 127.0.0.1。您不需要对它进行任何配置。
  • enp1s0: 这就是您的物理网卡接口 (Ethernet card)。根据命名规则,这很可能是一个PCI Express插槽上的网卡。您需要配置的就是这个接口。

我选择使用DHCP,因为这样配置起来最简单。当我把服务器挪到有网络的地方并开机后,系统会通过 enp1s0 这个网卡向网络中的DHCP服务器(通常是路由器)请求IP地址。

以下是在有显示器的环境下,需要为 enp1s0 接口配置动态IP的方法:


这是在Debian上最经典的方法,兼容性最好。

1、编辑配置文件
用 nano 或 vim 编辑器打开配置文件:

sudo nano /etc/network/interfaces

2、修改或添加配置
确保文件中有以下内容,用于配置 enp1s0 接口。如果文件是空的或者有其他关于此接口的静态配置,可以先将其注释掉(在行首加 #)或删除。

# The primary network interface
auto enp1s0
allow-hotplug enp1s0
iface enp1s0 inet dhcp
  • auto enp1s0: 表示在系统启动时自动激活 enp1s0 接口。
  • allow-hotplug enp1s0: 表示在检测到网线插入时自动激活 enp1s0 接口。
  • iface enp1s0 inet dhcp: 表示 enp1s0 接口通过DHCP来获取IPv4地址。

3、保存并重启网络服务
保存文件后,可以重启网络服务来使配置立即生效,或者直接重启服务器

sudo systemctl restart networking.service

1、安装 Docker 前的准备工作

在开始安装之前,必须完成以下准备步骤,以确保安装过程顺利且系统环境干净。

1. 确认系统要求

  • 操作系统: 必须是 64 位版本的以下 Debian 系统之一:
    • Debian 13 (Trixie) - 测试版
    • Debian 12 (Bookworm) - 稳定版 (推荐)
    • Debian 11 (Bullseye) - 旧稳定版
  • 架构: 支持 x86_64 (也叫 amd64), armhfarm64 等主流架构。

2. 卸载旧版本或冲突的软件包

您的系统可能通过 Debian 自带的软件源安装了非官方的 Docker 包,或者存在其他冲突软件。必须先将它们卸载。

需要卸载的包包括docker.iodocker-docdocker-composepodman-dockercontainerdrunc

使用以下命令一键卸载所有可能冲突的包:

# 循环遍历并卸载列表中的每一个包
for pkg in docker.io docker-doc docker-compose podman-docker containerd runc; do sudo apt-get remove $pkg; done
  • 提示: 如果系统提示某个包未安装,这是正常现象,直接忽略即可。
  • 注意: 此操作不会删除您已有的 Docker 镜像、容器或卷(数据存储在 /var/lib/docker/ 目录下)。

3. 理解防火墙的特殊情况(重要警告)

  • 如果您使用 ufw 或 firewalld 来管理防火墙,请注意:当 Docker 映射容器端口时,会绕过这些防火墙规则!
  • 这意味着,即使您在 ufw 中禁止了某个端口,如果 Docker 将其映射出来,该端口依然可以被外界访问。这是 Docker 网络机制的一个重要特性,需要特别留意其安全影响。

2、安装 Docker Engine (推荐方法)

官方提供了多种安装方法,但最推荐的是使用 Docker 官方的 apt 软件仓库进行安装。这样做的好处是,您可以轻松地进行安装和后续的升级。

步骤 1: 设置 Docker 的官方 apt 仓库

这个过程是为了让您的系统信任并知道从哪里下载官方的 Docker 软件包。

# 1. 更新现有的包列表并安装必要的工具
sudo apt-get update
sudo apt-get install ca-certificates curl

# 2. 创建用于存放 GPG 密钥的目录
sudo install -m 0755 -d /etc/apt/keyrings

# 3. 下载 Docker 的官方 GPG 密钥(用于验证软件包的真实性)
sudo curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc

# 4. 更改密钥文件的权限,确保 apt 可以读取它
sudo chmod a+r /etc/apt/keyrings/docker.asc

# 5. 将 Docker 的软件源添加到系统的 apt 源列表中
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# 6. 再次更新 apt 包列表,以包含新添加的 Docker 软件包信息
sudo apt-get update

步骤 2: 安装 Docker 相关软件包

现在,您可以直接从刚刚配置好的 Docker 仓库中安装最新版本的 Docker 了。

# 安装 Docker Engine, 命令行工具, containerd, 以及 buildx 和 compose 插件
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
  • docker-ce: Docker 社区版(核心引擎)。
  • docker-ce-cli: Docker 命令行工具。
  • containerd.io: 容器运行时。
  • docker-buildx-plugindocker-compose-plugin: 官方推荐的插件,让您可以使用 docker buildx 和 docker compose 命令。

步骤 3: 验证安装是否成功

通过运行一个经典的 hello-world 镜像来测试 Docker 是否已正确安装并运行。

sudo docker run hello-world

如果安装成功,您会看到一条来自 hello-world 容器的欢迎信息,其中包含 "Hello from Docker!"。这表明 Docker 已成功下载镜像、启动容器并正确执行。


3、安装后的重要配置(推荐)

将当前用户添加到 docker 用户组,实现免 sudo 执行 Docker 命令

默认情况下,您必须使用 sudo 才能运行 Docker 命令。为了方便,可以把当前用户加入 docker 组。

1、执行添加命令

# 将您当前的用户($USER)添加到 docker 用户组中
sudo usermod -aG docker $USER

2、重新登录或激活配置

这一步至关重要! 您必须退出当前的终端会话并重新登录(或者重启系统),这样新的用户组权限才会生效。之后,您就可以直接运行 docker ps 等命令,而无需再加 sudo


4、其他操作

如何升级 Docker Engine

如果您是按照前面推荐的方法安装的,升级非常简单:

# 1. 更新软件包列表
sudo apt-get update

# 2. 直接运行安装命令即可安装到最新版本
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

如何彻底卸载 Docker Engine

1、卸载软件包

sudo apt-get purge docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin docker-ce-rootless-extras

使用 purge 会同时移除相关的配置文件。

2、删除所有 Docker 数据(危险操作)
卸载软件包并不会删除镜像、容器、卷等数据。如果您想彻底清理,请手动删除以下目录:

# 警告:此操作会删除您所有的容器和镜像数据,且不可恢复!
sudo rm -rf /var/lib/docker
sudo rm -rf /var/lib/containerd

3、清理软件源和密钥

sudo rm /etc/apt/sources.list.d/docker.list
sudo rm /etc/apt/keyrings/docker.asc

附录:其他安装方法简介

  • 手动安装 .deb 包: 适用于无法连接 Docker 官方仓库的离线环境。您需要去官网手动下载所有 containerddocker-cecli 等 deb 包,然后使用 sudo dpkg -i ... 命令进行安装。升级和管理会比较繁琐。
  • 使用便捷脚本 (get.docker.com): 官方提供了一键安装脚本。但不推荐在生产环境中使用,因为它会自动执行许多操作,缺乏控制。它更适合用于快速搭建测试环境。

第二步:获取 Misskey 源代码

此步骤的目的是从 GitHub(一个代码托管平台)上将 Misskey 的最新版源代码下载到您的服务器上。

# 从官方仓库下载 Misskey 的主分支代码
git clone -b master https://github.com/misskey-dev/misskey.git

# 进入刚刚下载好的 misskey 文件夹
cd misskey

# 确保当前在主分支上(这步是为确保万无一失)
git checkout master

第三步:创建并编辑配置文件

程序需要配置文件才能知道如何运行,比如您的域名是什么,数据库密码是多少。此步骤就是从官方提供的模板文件复制出我们自己要用的配置文件。

1、执行复制命令

# 复制 Misskey 主配置文件
cp .config/docker_example.yml .config/default.yml

# 复制 Docker 环境变量文件(通常用于存放密码等敏感信息)
cp .config/docker_example.env .config/docker.env

# 复制 Docker Compose 的部署文件
cp ./compose_example.yml ./compose.yml

2、编辑配置文件(这是关键步骤)

您需要使用文本编辑器(如 nano 或 vim)打开并修改以下两个文件:

  • default.yml: 这是 Misskey 的核心配置文件。您必须根据文件内的注释说明,修改以下必填项
    • url: 填写您准备给这个网站使用的完整域名,例如 https://example.com
  • docker.env: 这个文件专门存放数据库密码。请务必修改里面的密码,设置一个足够复杂的密码以保证安全。

另外,compose.yml 文件定义了需要启动哪些服务。一般情况下您不需要修改它,除非您想更改对外服务的端口(默认为 3000)。

第四步:构建应用并初始化数据库

此步骤包含两个重要操作:编译打包应用和创建数据库结构。

# 1. 构建镜像:Docker 会根据代码和配置,打包成一个独立的、可运行的应用镜像。
#    这个过程需要下载大量依赖,会花费很长时间,请耐心等待。
sudo docker compose build

# 2. 初始化数据库:运行一个临时容器来执行初始化脚本。
#    这个脚本会连接到数据库,并创建运行 Misskey 所需的各种数据表。
#    --rm 参数意味着命令执行完后会自动删除这个临时容器。
sudo docker compose run --rm web pnpm run init

再次强调:第二条 init 命令是一次性操作,请勿在日后重复执行。

第五步:启动 Misskey 服务

恭喜!完成以上步骤后,就可以正式启动您的网站了。

# 使用 up 命令启动所有服务,-d 参数表示在后台持续运行。
sudo docker compose up -d

执行后,稍等片刻,您就可以通过 http://你的服务器IP:3000 或您在 default.yml 中配置的域名来访问您的 Misskey 实例了。


日常维护操作

如何更新 Misskey 版本

重要:更新前,请务必抽空阅读官方的更新日志 (CHANGELOG.md),确认新版本是否有重大变更或需要额外的手动操作。

在服务器的 misskey 目录下,依次执行以下命令:

# 暂存您对配置文件的修改,以防与新代码冲突
git stash
# 切换到主分支并拉取最新的代码
git checkout master
git pull
# 更新项目的子模块依赖
git submodule update --init
# 恢复您之前暂存的配置
git stash pop
# 根据新代码重新构建应用镜像
sudo docker compose build
# 停止旧服务,然后用新镜像启动服务,完成更新
sudo docker compose stop && sudo docker compose up -d

如何执行后台管理命令 (CLI)

有时您可能需要执行一些特殊管理任务(如重置用户密码、清理数据等),这需要通过后台命令行工具 (CLI) 完成。

执行语法

# 格式为:sudo docker compose run --rm web [具体命令]
# 文档中的示例是执行一个名为 foo 的工具,并传入 bar 参数
sudo docker compose run --rm web node packages/backend/built/tools/foo bar

您只需将示例中的 node ... foo bar 部分替换为您实际需要执行的管理命令即可。

可能会遇到的一些问题

上传图片时报错 Internal Server Error

文件夹权限问题,需在 Misskey 根文件夹运行 sudo chown -R 991:991 files

通过内网穿透访问网站一直加载中,但局域网内可以打开正常

是套了 Cloudflare CDN 的问题,登录 Cloudflare 主面板,进入 Speed -> OptimizationRocket Loader™: 确保这个功能是关闭的。它非常容易破坏 Web 应用。

对于任何复杂的、基于现代 JavaScript 框架的 Web 应用(不仅仅是 Misskey),一个通用的最佳实践是:在 Cloudflare 或任何类似的 CDN 服务中,务必禁用任何会自动重排或延迟加载 JavaScript 的“性能优化”功能,特别是像 Rocket Loader™ 这样的。

这些现代应用本身已经通过构建工具(如 Vite, Webpack)进行了高度优化,外部工具的再次“优化”往往会适得其反。

在 Docker 及 Cloudflare Tunnel 环境下无法联合(关注外站用户失败)

一、问题背景与症状

在通过 Docker 部署 Misskey 实例,并使用 Cloudflare Tunnel(cloudflared)进行内网穿透后,遇到了以下几个核心问题:

  1. 联合失败:尝试关注任何外部 Misskey 服务器的用户(例如 @[email protected])时,立即提示错误。
  2. 搜索失效:站内搜索功能无法搜索到任何外部实例的用户或内容。
  3. 全局时间轴(GTL)空空如也:即使已经配置好,全局时间轴上也没有任何外部内容。

这些症状共同指向一个根本原因:我的 Misskey 服务器无法与外部世界进行出站(Outbound)网络通信。


二、排查之旅:从现象到本质

Step 1: 初步诊断 - 查看应用日志

任何问题排查的第一步,都是听听应用程序自己怎么说。我们通过 Docker 日志来获取第一手信息。

操作:

# 实时跟踪 Misskey web 服务的日志
sudo docker compose logs -f web

在跟踪日志的同时,在浏览器中尝试进行一次失败的“关注”操作。

关键发现:
日志中出现了明确的错误信息:

ERR * [remote resolve-user] Failed to ... reason: Blocked address: 198.18.3.114

分析与讲解:

  • Blocked address:这不是一个网络连接错误(如 ETIMEDOUT),而是 Misskey 出于安全考虑(防止SSRF攻击)主动阻止了这次请求。
  • 198.18.3.114:这是一个用于网络基准测试的保留IP地址段。
  • 初步结论:当 Misskey 尝试查找外部域名(如 misskey.io)时,DNS 解析返回了一个奇怪的、被列入黑名单的内部 IP。这强烈暗示了 DNS 解析过程受到了干扰。

Step 2: 验证推论 - 进入容器内部测试网络

为了验证 DNS 是不是真的出了问题,我们需要直接进入容器的“肚子”里,以它的视角来访问外部网络。

操作:

# 进入 web 容器的命令行环境
sudo docker compose exec web bash # 或 sh

# 在容器内部尝试用 curl 访问外部网站
curl -v https://misskey.io

关键发现:
curl 命令的输出带来了两个重磅消息:

  1. * Trying 198.18.3.115:443...验证了 DNS 劫持。在容器内部,所有对公网域名的请求都被解析到了 198.18.x.x 这个奇怪的地址段。这通常是 cloudflared 服务接管了宿主机的 DNS 所导致的。
  2. curl: (77) error setting certificate file: /etc/ssl/certs/ca-certificates.crt发现了第二个问题。即使 DNS 问题解决了,容器内部也缺少用于验证 HTTPS 连接的根证书文件,导致所有 https:// 请求都会失败。

分析:
我们现在面临两个独立的问题:

  1. DNS 问题:Docker 默认使用了被 cloudflared 干扰的宿主机 DNS。
  2. 证书问题:Misskey 的 Docker 镜像过于精简,没有包含 ca-certificates 包。

Step 3: 解决 DNS 问题 - 配置 Docker 全局 DNS

我们首先解决最主要的问题。我们需要强制所有 Docker 容器使用可靠的公共 DNS 服务器,绕过宿主机的干扰。

操作:

1、创建或编辑 Docker 的配置文件 daemon.json

```bash
sudo nano /etc/docker/daemon.json

2、在文件中写入以下内容:

{
  "dns": ["8.8.8.8", "1.1.1.1"]
}

3、重启 Docker 服务使配置生效。

sudo systemctl restart docker

4、重启 Misskey 服务以应用新的网络配置。

sudo docker compose down && sudo docker compose up -d

验证:
再次进入容器内部执行 curl -v https://misskey.io,此时 * Trying ... 后面已经变成了真实的公网 IP。第一个问题成功解决! 但 curl: (77) 证书错误依然存在。


Step 4: 解决证书问题 - 尝试通过 Dockerfile 安装

我们尝试通过修改 Dockerfile,在镜像构建时就将证书包装进去。

操作(这是一个尝试,但最终证明在复杂的多阶段构建中无效):

  1. 编辑 Dockerfile,在构建最终运行镜像的 runner 阶段,为 apt-get install 命令添加 ca-certificates 包。
  2. 执行 sudo docker compose up -d --build 进行重建。

关键发现:
问题依旧。通过 sudo docker compose exec web bash 进入容器内部,使用 ls -l /etc/ssl/certs/ca-certificates.crt 和 ls -l /etc/ssl/certs/ | head -n 10 以及 dpkg -l | grep ca-certificates 命令检查后发现,/etc/ssl/certs 目录、/etc/ssl/certs/ca-certificates.crt 文件和 ca-certificates 包在最终的运行环境中根本不存在

分析:
Misskey 使用的先进的多阶段构建 Dockerfile 非常复杂,它将构建环境和运行环境严格分离。我们在一个阶段安装的东西,可能并不会被拷贝到最终的运行阶段。直接修改 Dockerfile 的方法在此场景下失效。


Step 5: 最终解决方案 - 在容器启动时动态修复环境

既然无法在“出厂前”(构建时)修复,那我们就在“每次开机时”(启动时)进行动态修复。我们将利用 compose.yml 的 command 和 user 指令,实现一个“自检修复”的启动脚本。

操作:

1、编辑 compose.yml 文件。

2、找到 web: 服务,添加/修改 user 和 command 两个指令,将其替换为以下内容:

```yaml
# ... web 服务其他配置 ...
  user: root  # 1. 强制以 root 身份启动,获得权限
  command: >  # 2. 覆盖默认启动命令,执行我们的修复脚本
    bash -c "
      # 3. 检查证书包是否已安装
      if ! dpkg -l | grep -q ca-certificates; then
        echo '>>>> Certificate package not found. Installing now...'
        # 4. 如果未安装,就以 root 权限进行安装
        apt-get update && apt-get install -y ca-certificates
      fi &&
      echo '>>>> Starting Misskey as user misskey...' &&
      # 5. 修复完成后,切换回低权限的 misskey 用户,再安全地启动程序
      su misskey -c 'pnpm run migrateandstart'
    "

3、应用新的配置并重启服务。

sudo docker compose down && sudo docker compose up -d

最终验证:
再次进入容器内部 sudo docker compose exec web curl -v https://misskey.io,命令成功返回网页内容。回到浏览器,关注功能恢复正常。问题解决。


三、结论与总结

这次问题的根源是 cloudflared 服务的 DNS 劫持 和 Misskey 官方 Docker 镜像的极度精简 两个因素叠加造成的。通过层层递进的诊断,我们最终绕过了复杂的 Docker 构建过程,采用在容器启动时动态注入修复逻辑的方式,一劳永逸地解决了问题。经测试,可以联合(关注外站用户)成功:

这个案例告诉我们,在处理容器化应用的网络问题时:

  1. 日志是永远的起点。
  2. docker exec 是深入现场、验证推论的终极武器。
  3. 当构建时修复(Dockerfile)过于复杂或无效时,在运行时修复(compose.yml command)往往是更直接、更有效的降维打击。

Misskey 搜索提示“帖子检索不可用”

搜索功能一直提示“帖子检索不可用”,最后发现是默认没有开搜索功能导致的。需要用超级管理员账号(即部署后创建的第一个账号),路径:控制面板-角色-基本角色,在下拉列表中找到“是否可以搜索帖子”,设置为“是”,重新加载/切换一下账号即可解决。这个地方还可以设置各种细节,真是个完善的去中心化平台。