搭建 Mastodon，加入联邦社交网络

Mastodon 是一个基于 ActivityPub 协议的自由、开源社交网络服务器，用户可以关注朋友并发现新的朋友。在 Mastodon 上，用户可以发布任何内容，包括链接、图片、文字和视频等。所有 Mastodon 服务器都可以互相通信，形成一个联合网络，即使使用实现了 ActivityPub 协议的非 Mastodon 软件，也可以与 Mastodon 用户无缝交流！

Mastodon 是一个去中心化的社交网络，这意味着每个用户可以选择自己的服务器（或 “实例”）并控制自己的数据，同时仍然成为 Mastodon 用户的一部分。由于 Mastodon 基于 ActivityPub 协议，它还可以与使用相同协议的其他去中心化社交网络进行通信，例如 Pleroma、Friendica 和 Hubzilla 等。这种互操作性有助于促进更多样化和弹性的在线社区，使用户不会被限制在单个集中式平台或公司中。

下面简单介绍在一台 VPS 上搭建 Mastodon 的方式（推荐至少 1C2G，基于 Debian 11 root 用户）：

1/ 安装 Docker 及 Compose

一条命令：

Copy
curl -L get.docker.com | bash

2/ 安装 Nginx 及 ACME.SH

这里推荐由 n.wtf 打包的 Nginx：

Copy
# Install required software
apt install -y lsb-release ca-certificates apt-transport-https curl gnupg dpkg

# Download PGP Key
curl -sS https://n.wtf/public.key | gpg --dearmor > /usr/share/keyrings/n.wtf.gpg

# Add repo
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/n.wtf.gpg] https://mirror-cdn.xtom.com/sb/nginx/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/n.wtf.list

# Update system
apt update

# Install Latest Nginx
apt install nginx-extras -y

安装 acme.sh，一条命令：

Copy
curl -L get.acme.sh | bash

安装完成后，您可能需要关闭当前终端并重新打开它，以使 acme.sh 别名生效。

3/ 安装 Mastodon

新建文件夹

Copy
mkdir -p /var/www/mastodon && cd /var/www/mastodon

编辑 docker-compose.yml

Copy
vim docker-compose.yml

按 i 开始编辑，按 Shift+Ins 粘贴，按 Esc 输入 :wq 保存退出

Copy
version: '3'
services:
db:
restart: always
image: postgres:12.5-alpine
shm_size: 256mb
networks:
- internal_network
healthcheck:
test: ["CMD", "pg_isready", "-U", "postgres"]
volumes:
- ./postgres:/var/lib/postgresql/data

redis:
restart: always
image: redis:6-alpine
networks:
- internal_network
healthcheck:
test: ["CMD", "redis-cli", "ping"]
volumes:
- ./redis:/data

# es:
# image: kubesphere/elasticsearch-oss:6.7.0-1-arm64
# environment:
# - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
# - "cluster.name=es-mastodon"
# - "discovery.type=single-node"
# - "bootstrap.memory_lock=true"
# networks:
# - internal_network
# healthcheck:
# test: ["CMD-SHELL", "curl --silent --fail localhost:9200/_cluster/health || exit 1"]
# volumes:
# - ./elasticsearch:/usr/share/elasticsearch/data
# ulimits:
# memlock:
# soft: -1
# hard: -1
# restart: unless-stopped

web:
image: plusminusio/mastodon:latest-arm64
restart: always
env_file: .env.production
command: bash -c "rm -f /mastodon/tmp/pids/server.pid; bundle exec rails s -p 3000"
networks:
- external_network
- internal_network
healthcheck:
test: ["CMD-SHELL", "wget -q --spider --proxy=off localhost:3000/health || exit 1"]
ports:
- "127.0.0.1:3000:3000"
depends_on:
- db
- redis
# - es
volumes:
- ./public/system:/mastodon/public/system

streaming:
image: plusminusio/mastodon:latest-arm64
restart: always
env_file: .env.production
command: node ./streaming
networks:
- external_network
- internal_network
healthcheck:
test: ["CMD-SHELL", "wget -q --spider --proxy=off localhost:4000/api/v1/streaming/health || exit 1"]
ports:
- "127.0.0.1:4000:4000"
depends_on:
- db
- redis

sidekiq:
image: plusminusio/mastodon:latest-arm64
restart: always
env_file: .env.production
command: bundle exec sidekiq
depends_on:
- db
- redis
networks:
- external_network
- internal_network
volumes:
- ./public/system:/mastodon/public/system

networks:
external_network:
internal_network:
internal: true

配置数据库

Copy
docker run --name postgres12 -v /var/www/mastodon/postgres:/var/lib/postgresql/data -e POSTGRES_PASSWORD=设置数据库管理员密码 --rm -d postgres:12.5-alpine

检查 postgres 文件夹，应该出现 postgres 相关的多个文件，不是空文件夹。

然后执行

Copy
docker exec -it postgres12 psql -U postgres

输入

Copy
CREATE USER mastodon WITH PASSWORD '数据库密码（最好和数据库管理员密码不一样）' CREATEDB;

创建 mastodon 用户，然后停止 docker

Copy
docker stop postgres12

配置 .env.production

Copy
# 回到 mastodon 文件夹
cd /var/www/mastodon

# 生成文件
touch .env.production

# 配置文件
docker compose run --rm web bundle exec rake mastodon:setup

输入域名

Enable single user mode? 否

Using Docker to run Mastodon? 是

postsql 用户名、数据库名填 mastodon ，密码部分填刚刚设置的数据库密码

redis 部分都直接回车

Store uploaded files on the cloud? 这个可填否，如有需要可配置 S3。

Send e-mails from localhost? 否。然后填入邮件服务设置，推荐使用 Mailazy，具体配置可参照下方。

This configuration will be written to .env.production Save configuration? 是

然后会出现 .env.production 配置，复制下来，先存到电脑里，等会用。

然后会要你建立数据库和编译，都选是。最后建立管理员账号。

成功之后，编辑 .env.production 文件，按 i 开始编辑，按 Shift+Ins 粘贴，按 Esc 输入 :wq 保存退出

Copy
vim .env.production

S3 及 SMTP 信息大致如下

Copy
S3_ENABLED=true
S3_PROTOCOL=https
S3_REGION=fr-par
S3_ENDPOINT=https://s3.fr-par.scw.cloud
S3_HOSTNAME=[hidden].s3.fr-par.scw.cloud
S3_BUCKET=[hidden]
AWS_ACCESS_KEY_ID=[hidden]
AWS_SECRET_ACCESS_KEY=[hidden]
S3_ALIAS_HOST=[hidden]
SMTP_SERVER=smtp.mailazy.com
SMTP_PORT=587
SMTP_LOGIN=[hidden]
SMTP_PASSWORD=[hidden]
SMTP_AUTH_METHOD=plain # 该配置仅代表可用于 mailazy，若为其他服务商请做相应更改
SMTP_OPENSSL_VERIFY_MODE=none # 该配置仅代表可用于 mailazy，若为其他服务商请做相应更改
SMTP_FROM_ADDRESS=mastodon@[hidden]

运行

Copy
docker compose up -d

为相应文件夹赋权

Copy
chown 991:991 -R ./public
chown -R 70:70 ./postgres

# 关闭
docker compose down

# 再次启动
docker compose up -d

查看运行详情

Copy
docker compose ps

Copy
NAME COMMAND SERVICE STATUS PORTS
mastodon-db-1 "docker-entrypoint.s…" db running (healthy)
mastodon-redis-1 "docker-entrypoint.s…" redis running (healthy)
mastodon-sidekiq-1 "/usr/bin/tini -- bu…" sidekiq running 4000/tcp
mastodon-streaming-1 "/usr/bin/tini -- no…" streaming running (healthy) 127.0.0.1:4000->4000/tcp
mastodon-web-1 "/usr/bin/tini -- ba…" web running (healthy) 127.0.0.1:3000->3000/tcp

# 当状态均为 healthy 的时候即代表成功运行

4/ 配置 Nginx 及 SSL

创建配置文件

Copy
vim /etc/nginx/conf.d/mastodon.conf

复制下方 http 配置信息以申请 SSL 证书，按 i 开始编辑，按 Shift+Ins 粘贴，按 Esc 输入 :wq 保存退出

Copy
server {
listen 80;
# 若配置了 IPV6，删除下方的井号
# listen [::]:80;
server_name mastodon.im.sb;
root /var/www/mastodon/public;
location /.well-known/acme-challenge/ { allow all; }
location / { return 301 https://$host$request_uri; }
}

# 将 mastodon.im.sb 替换成您自己的域名

重载 Nginx

Copy
nginx -t
# 确保配置文件无异常
# nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
# nginx: configuration file /etc/nginx/nginx.conf test is successful

# 重载
nginx -s reload

申请 SSL 证书

将您的域名 A 记录及 AAAA 记录（若有）指向 VPS IP，然后申请域名证书

Copy
acme.sh --issue -d mastodon.im.sb -w /var/www/mastodon/public --server letsencrypt

# 将 mastodon.im.sb 替换成您自己的域名

申请成功后，我们需要其中的：

Copy
# SSL 完整证书
/root/.acme.sh/mastodon.im.sb_ecc/fullchain.cer
# SSL 私钥
/root/.acme.sh/mastodon.im.sb_ecc/mastodon.im.sb.key

再次编辑配置文件

Copy
vim /etc/nginx/conf.d/mastodon.conf

复制下方完整配置信息，按 i 开始编辑，按 Shift+Ins 粘贴，按 Esc 输入 :wq 保存退出

Copy
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}

upstream backend {
server 127.0.0.1:3000 fail_timeout=0;
}

upstream streaming {
server 127.0.0.1:4000 fail_timeout=0;
}

server {
listen 80;
# 若配置了 IPV6，删除下方的井号
# listen [::]:80;
server_name mastodon.im.sb;
root /var/www/mastodon/public;
location /.well-known/acme-challenge/ { allow all; }
location / { return 301 https://$host$request_uri; }
}

server {
listen 443 ssl http2;
# 若配置了 IPV6，删除下方的井号
# listen [::]:443 ssl http2;
server_name mastodon.im.sb;

ssl_certificate /root/.acme.sh/mastodon.im.sb_ecc/fullchain.cer;
ssl_certificate_key /root/.acme.sh/mastodon.im.sb_ecc/mastodon.im.sb.key;

ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
ssl_ciphers TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-128-GCM-SHA256:EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+ECDSA+AES128:EECDH+aRSA+AES128:RSA+AES128:EECDH+ECDSA+AES256:EECDH+aRSA+AES256:RSA+AES256:EECDH+ECDSA+3DES:EECDH+aRSA+3DES:RSA+3DES:!MD5;
ssl_prefer_server_ciphers off;
ssl_ecdh_curve X25519:secp384r1;

ssl_session_cache shared:MASTODON:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;

ssl_stapling on;
ssl_stapling_verify on;
resolver 1.1.1.1 8.8.8.8 119.29.29.29 valid=300s;
resolver_timeout 5s;

root /var/www/mastodon/public;

gzip on;
gzip_disable "msie6";
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

add_header Strict-Transport-Security "max-age=31536000" always;

keepalive_timeout 70;
sendfile on;
client_max_body_size 80m;

location / {
try_files $uri @proxy;
}

location ~ ^/(emoji|packs|system/accounts/avatars|system/media_attachments/files) {
add_header Cache-Control "public, max-age=31536000, immutable";
add_header Strict-Transport-Security "max-age=31536000" always;
try_files $uri @proxy;
}

location /sw.js {
add_header Cache-Control "public, max-age=0";
add_header Strict-Transport-Security "max-age=31536000" always;
try_files $uri @proxy;
}

location @proxy {
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 $scheme;
proxy_set_header Proxy "";
proxy_pass_header Server;

proxy_pass http://backend;
proxy_buffering on;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;

add_header X-Cached $upstream_cache_status;
add_header Strict-Transport-Security "max-age=31536000" always;

tcp_nodelay on;
}

location /api/v1/streaming {
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 $scheme;
proxy_set_header Proxy "";

proxy_pass http://streaming;
proxy_buffering off;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;

tcp_nodelay on;
}

error_page 403 404 500 501 502 503 504 /500.html;
}

# 将 mastodon.im.sb 替换成您自己的域名

重载 Nginx

Copy
nginx -t
# 确保配置文件无异常
# nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
# nginx: configuration file /etc/nginx/nginx.conf test is successful

# 重载
nginx -s reload

现在访问您的域名即可访问 Mastodon。

5/ 设置管理员

访问您的 Mastodon 并注册一个账号，返回终端运行：

Copy
# 回到 mastodon 文件夹
cd /var/www/mastodon

Copy
docker compose run --rm web bin/tootctl accounts modify [YOURACCOUNT] --role Owner

或者直接在终端创建一个管理员账号：

Copy
docker compose run --rm web bin/tootctl accounts create [YOURACCOUNT] --email [YOUR@EMAIL.COM] --confirmed --role Owner

将在终端中显示一个随机生成的密码。

👉 部分内容参考了此篇文章。