Mastodon 快速搭建教程(一)

X(Twitter) 最近公布的新政策让我逐步迁移到了其他社交平台,恰巧在朋友那里了解到了 Mastodon 这个去中心化的社交平台,于是决定自建一个。

我一开始打算是直接从源码构建部署,但后来发现太麻烦且不方便维护,于是选择了使用 Docker Compose 快速部署。

0.前期准备

  • 一台性能过得去的服务器
  • 安装了 Docker 和 Docker-Compose
  • (可选,推荐) 提前配置 Postgres 数据库
  • (可选) 提前配置 Redis

1.获取和配置 Mastodon

1
2
3
mkdir mastodon
cd mastodon
wget https://raw.githubusercontent.com/mastodon/mastodon/refs/heads/main/docker-compose.yml

然后打开 docker-compose.yml 文件,主要修改以下部分:

  • 修改 image: ghcr.io/mastodon/mastodon: 后面的版本号,改为 latest 或是你想要的版本即可
  • 4.3.0 的镜像可能存在部署问题,建议使用 4.3.1 的镜像(站长亲自踩坑)
  • 如果你已经有了自建的 Postgres 和 Redis 实例,那么请注释掉 dbredis 部分
  • 剩下的可以根据自身情况修改 (比如我就删掉了 internal_network)

然后执行:

1
2
touch .env.production
docker-compose run --rm web bundle exec rake mastodon:setup

接着会进入到配置生成流程,

如果你没有把 Postgres 和 Redis 分离部署,则PostgreSQL的默认用户名是postgres,密码直接回车,Redis 部分直接回车。

否则请根据你实际情况填写。

邮件服务器 SMTP 请根据实际情况填写。

流程参考如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
[+] Creating 2/0
✔ Container mastodon-db-1 Running 0.0s
✔ Container mastodon-redis-1 Running 0.0s
Your instance is identified by its domain name. Changing it afterward will break things.
Domain name: baka.ink

Single user mode disables registrations and redirects the landing page to your public profile.
Do you want to enable single user mode? yes

Are you using Docker to run Mastodon? Yes

PostgreSQL host: db
PostgreSQL port: 5432
Name of PostgreSQL database: postgres
Name of PostgreSQL user: postgres
Password of PostgreSQL user:
Database configuration works! 🎆

Redis host: redis
Redis port: 6379
Redis password:
Redis configuration works! 🎆

Do you want to store uploaded files on the cloud? No

Do you want to send e-mails from localhost? No
SMTP server: smtp.example.com
SMTP port: 587
SMTP username: Admin
SMTP password: 123456
SMTP authentication: plain
SMTP OpenSSL verify mode: peer
Enable STARTTLS: always
E-mail address to send e-mails "from": Mastodon <[email protected]>
Send a test e-mail with this configuration right now? no

Do you want Mastodon to periodically check for important updates and notify you? (Recommended) Yes

This configuration will be written to .env.production
Save configuration? Yes
Below is your configuration, save it to an .env.production file outside Docker:

配置文件可能会自动写入到 .env.production,但也可能不会写,因此请手动编辑 .env.production 写入输出的配置。

2.运行 Mastodon

启动 Mastodon:

1
docker-compose up -d

为相应文件夹赋权:

1
2
3
4
chown 991:991 -R ./public
chown -R 70:70 ./postgres14
docker-compose down
docker-compose up -d

3.配置反向代理

如果你使用的是 Nginx,请直接参考官方配置 Github,需要把所有的 try_files $uri =404; 替换为 try_files $uri @proxy

如果你使用的是 OpenResty,则可以参考我的配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}

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

upstream streaming {
# Instruct nginx to send connections to the server with the least number of connections
# to ensure load is distributed evenly.
least_conn;

server 127.0.0.1:4000 fail_timeout=0;
# Uncomment these lines for load-balancing multiple instances of streaming for scaling,
# this assumes your running the streaming server on ports 4000, 4001, and 4002:
# server 127.0.0.1:4001 fail_timeout=0;
# server 127.0.0.1:4002 fail_timeout=0;
}

proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=CACHE:10m inactive=7d max_size=1g;

server {
listen 80;
listen [::]:80;
server_name baka.ink *.baka.ink; # 修改成你自己的网站
root /root/mastodon/public; # May Change
location /.well-known/acme-challenge/ { allow all; }
location / { return 301 https://$host$request_uri; }
}


server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name baka.ink *.baka.ink; # 修改成你自己的网站
ssl_certificate /www/sites/baka.ink/ssl/fullchain.pem; # 修改成你自己的网站 SSL
ssl_certificate_key /www/sites/baka.ink/ssl/privkey.pem; # 修改成你自己的网站 SSL
ssl_protocols TLSv1.3 TLSv1.2 TLSv1.1 TLSv1;
ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:!aNULL:!eNULL:!EXPORT:!DSS:!DES:!RC4:!3DES:!MD5:!PSK:!KRB5:!SRP:!CAMELLIA:!SEED;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;

keepalive_timeout 70;
sendfile on;
client_max_body_size 99m;

root /root/mastodon/public; # May Change

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 image/svg+xml image/x-icon;

location / {
try_files $uri @proxy;
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';

if ($request_method = 'OPTIONS') {
return 204;
}
}


# If Docker is used for deployment and Rails serves static files,
# then needed must replace line `try_files $uri @proxy;` with `try_files $uri @proxy;`

location ~ ^/assets/ {
add_header Cache-Control "public, max-age=2419200, must-revalidate";
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
try_files $uri @proxy;
}

location ~ ^/avatars/ {
add_header Cache-Control "public, max-age=2419200, must-revalidate";
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
try_files $uri @proxy;
}

location ~ ^/emoji/ {
add_header Cache-Control "public, max-age=2419200, must-revalidate";
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
try_files $uri @proxy;
}

location ~ ^/headers/ {
add_header Cache-Control "public, max-age=2419200, must-revalidate";
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
try_files $uri @proxy;
}

location ~ ^/packs/ {
add_header Cache-Control "public, max-age=2419200, must-revalidate";
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
try_files $uri @proxy;
}

location ~ ^/shortcuts/ {
add_header Cache-Control "public, max-age=2419200, must-revalidate";
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
try_files $uri @proxy;
}

location ~ ^/sounds/ {
add_header Cache-Control "public, max-age=2419200, must-revalidate";
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
try_files $uri @proxy;
}

location ~ ^/system/ {
add_header Cache-Control "public, max-age=2419200, immutable";
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
add_header X-Content-Type-Options nosniff;
add_header Content-Security-Policy "default-src 'none'; form-action 'none'";
try_files $uri @proxy;
}

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;

add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";

tcp_nodelay on;
}

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;

proxy_cache CACHE;
proxy_cache_valid 200 7d;
proxy_cache_valid 410 24h;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
add_header X-Cached $upstream_cache_status;

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

如果你的 OpenResty 跑在 Docker 里,别忘了把主机中 Your_Path/mastodon/public 映射到容器的 /root/mastodon/public 或是其他地方,并一并修改配置文件

4.其他

上面步骤应该能完成全部配置,如果你遇到问题不妨在评论区交流。

也欢迎你来我的实例玩:Baka.Ink

5.参考资料


Mastodon 快速搭建教程(一)
https://blog.byteloid.one/2024/11/01/docker部署mastodon/
作者
bingxin666
发布于
2024年11月2日
许可协议