[Mastodon] 使用 Docker 安裝開源分散式社群媒體服務的方法

本篇文章更新時間:2025/04/27
如有資訊過時或語誤之處,歡迎使用 Contact 功能通知。
一介資男的 LINE 社群開站囉!歡迎入群聊聊~
如果本站內容對你有幫助,歡迎使用 BFX Pay 加密貨幣新台幣 贊助支持。


這套 Mastodon 的社群媒體工具觀望很久,遲遲沒有下手安裝的原因,就是太複雜了啦!最原始的從原始碼安裝需求,會要 Ruby, PostgreSQL, NodeJS 基本環境之外,還要 Nginx, SSL, WebSocket 等網路環境的搭配,光打這些字都覺得累了!

不過還好,官方的 GitHub Repo 已經有提供方便安裝與啟動的 docker-compose.yml 檔案,也不需要自己 build image 映像檔,可以執行官方已經預先編譯好的映像檔。

以下進入使用 Docker 安裝這服務的輕鬆環節~

前置準備

  1. 至少要有 Docker 的運作環境
  2. Nginx 反向代理服務
  3. 用來識別位置的網域名稱

主機網路架構

畢竟是要做社群媒體站,一台可以上網的主機與公開的網路環境。

本篇我的架構稍微特別一點:

內網主機 (Docker + Nginx) <--- WireGuard ---> 外網主機 (Nginx) <-> Cloudflare (SSL)

內網主機利用 Docker + Nginx 把服務包在 Port 80 上,外網主機純粹做對外曝光的操作,Cloudflare 負責 SSL 憑證的處理與防火牆。

內網主機(Docker + Nginx)

系統是 Ubuntu 24.04 ,先是安裝 Docker:

curl -fsSL https://get.docker.com -o get-docker.sh && sh get-docker.sh

Nginx 我不是使用容器來處理,單純就是把這一層獨立安裝。基本上只要能接管主機網路 Port 80 做第一層轉發都可以。

再來開始安裝 Mastodon 環節:

# git clone https://github.com/mastodon/mastodon.git
# cd mastodon
# git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)
# cp .env.production.sample .env.production

寫文當前 Docker 版本為

Client: Docker Engine - Community
 Version:           28.0.4
 API version:       1.48
 Go version:        go1.23.7
 Git commit:        b8034c0
 Built:             Tue Mar 25 15:07:16 2025
 OS/Arch:           linux/amd64
 Context:           default

Server: Docker Engine - Community
 Engine:
  Version:          28.0.4
  API version:      1.48 (minimum version 1.24)
  Go version:       go1.23.7
  Git commit:       6430e49
  Built:            Tue Mar 25 15:07:16 2025
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.7.27
  GitCommit:        05044ec0a9a75232cad458027ca83437aae3f4da
 runc:
  Version:          1.2.5
  GitCommit:        v1.2.5-0-g59923ef
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

Mastodon 透過 git 指令切換最新版本,當前使用 v4.3.7

輸入下方指令啟用組態設定工具

docker compose run --rm web bundle exec rake mastodon:setup

需要準備好 Domain 與 SMTP 資訊(非必要,但做通知方便),過程會問一些問題,像是設定 Domain、設定 PostgreSQL 資料庫連線資訊、Redis 資料庫等,除非有客製化需求,不然問是不是正在使用 Docker 服務安裝的時候,選擇 yes,這些需要連線憑證來使用的服務都用預設方式,一步一步 Enter 即可。

一直到詢問結束前,會輸出一堆設定屬性參數值,並提問是否要儲存到 .env.production 檔案,這裡其實並不會去儲存,所以需要把設定值手動先覆寫 .env.production 檔案(開新視窗操作)。

然後才輸入「Yes」,讀取這個檔案,開始初始化資料庫。

到結束前,會設定 admin 管理員的帳號密碼,要記得最後輸出的密碼!

組態設定檔案範例如下:

WEB_DOMAIN=mxp.tw
LOCAL_DOMAIN=mxp.tw
SINGLE_USER_MODE=true
SECRET_KEY_BASE=SECRET_KEY_SECRET_KEY_SECRET_KEY_SECRET_KEY_SECRET_KEY_SECRET_KEY_SECRET_KEY_SECRET_KEY_
OTP_SECRET=SECRET_KEY_SECRET_KEY_SECRET_KEY_SECRET_KEY_SECRET_KEY_SECRET_KEY_SECRET_KEY_SECRET_KEY_SECRET_KEY_SECRET_KEY_SECRET_KEY_SECRET_KEY_
ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY=SECRET_KEY_SECRET_KEY_SECRET_KEY_SECRET_KEY_
ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT=SECRET_KEY_SECRET_KEY_SECRET_KEY_SECRET_KEY_
ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY=SECRET_KEY_SECRET_KEY_SECRET_KEY_SECRET_KEY_
VAPID_PRIVATE_KEY=SECRET_KEY_SECRET_KEY_SECRET_KEY_SECRET_KEY_-5_ZAM=
VAPID_PUBLIC_KEY=BPeHSFpcac87-SECRET_KEY_SECRET_KEY_SECRET_KEY_SECRET_KEY_-SECRET_KEY_SECRET_KEY_SECRET_KEY_SECRET_KEY_=
DB_HOST=db
DB_PORT=5432
DB_NAME=postgres
DB_USER=postgres
DB_PASS=
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_PASSWORD=
SMTP_SERVER=smtp.mailgun.org
SMTP_PORT=587
[email protected]
SMTP_PASSWORD=SECRET_KEY_SECRET_KEY_SECRET_KEY_SECRET_KEY_
SMTP_AUTH_METHOD=plain
SMTP_OPENSSL_VERIFY_MODE=none
SMTP_ENABLE_STARTTLS=auto
SMTP_FROM_ADDRESS=Mastodon 

WEB_DOMAIN 的參數預設不會出現,但這是 Mastodon 服務給的彈性,讓你可以「網站用子網域提供服務」但使用者完整 ID 能用「主網域」(感覺比較好看?)

到初始化完成資料庫(./postgres14)後,會結束組態檔案產生的畫面,接著編輯 docker-compose.yml 檔案,將其中對外阜號的 127.0.0.1:3000:3000 改為 3000:3000 ( streaming 的 127.0.0.1:4000 Port 也是照樣改)

上面這段修改如果沒有像我再多一層外網節點的連結其實就不必了。

完成後使用指令: docker compose up -d 初始化啟動服務。

接著要做一件修改權限的操作,我在這邊踩了個大雷,測了好久。因為檔案目錄權限不對,所以導致資料都無法寫入,而變成服務有開起來,但就是在資訊同步上出問題。

修正檔案權限

先取得使用者 ID,再用來修正權限。指令對應容器名稱與 mastodon 使用者,沒意外就是 991

# docker exec mastodon-web-1 id -u mastodon

然後 chown 991 -R ./public 把原始檔案目錄下的 public 目錄整個權限調整給容器使用者。

完成後重啟容器: docker compose restart

設定內網主機 Nginx 反向代理功能

現在服務開起來後主要有兩個對外的服務 3000 Port 的 Mastodon 網站服務,還有 4000 Port 的 streaming 服務(WebSocket)。

內網主機的 Nginx 設定如下:

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

server {
  listen 80;
  server_name mxp.tw;
  root /path/to/mastodon/public;

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

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

  location @proxy {
    proxy_set_header Host mxp.tw;
    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 https;
    proxy_set_header Proxy "";
    proxy_pass_header Server;

    proxy_pass http://127.0.0.1:3000;
    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;
  }

  location /api/v1/streaming {
    proxy_set_header Host mxp.tw;
    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 https;
    proxy_set_header Proxy "";

    proxy_pass http://127.0.0.1:4000;
    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;
  }

  location /.well-known/ {
    allow all;
    try_files $uri @proxy;
  }

  location / {
    try_files $uri @proxy;
  }
}

主要注意 proxy 的設定中 X-Forwarded-Proto 要是 https 所以儘管我用 80 的 HTTP 來傳輸,但還是要讓容器內的服務以為正在使用加密連線,避免無限重導向發生。

到這步,已經將整個 Mastodon 服務包裝好在內網主機,如果此時有其他區網主機要測試就已經可以正式使用。

接下來就是將此服務對外網開放。

外網主機(Nginx)

基本上到這步已經很簡單了,只需要將外部的請求轉發給內網主機的 Nginx 服務。

設定如下:

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

server {
  listen 80;
  server_name mxp.tw;

  location /api/v1/streaming {
    proxy_set_header Host mxp.tw;
    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 https;
    proxy_set_header Proxy "";
    proxy_pass http://10.10.10.2:4000;
    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;
  }

  location / {
    proxy_set_header Host mxp.tw;
    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 https;
    proxy_pass_header Server;
    proxy_pass http://10.10.10.2:80;
  }
}

這邊要注意的有兩點,Host 要對應到內網 Nginx 設定的網域,所有的請求都轉發給內網主機的 80 Port,但是 WebSocket 的請求要特別對應內網主機的 4000 Port。

Cloudflare (SSL)

這步驟使用 Cloudflare 主要兩個點:

  1. SSL 憑證我直接用他家的,省的處理 Let's Encrypt。
  2. 畢竟是社群媒體服務,前面還是掛一個 CDN+WAF 服務保險,還免費的,不香嗎!

不過要注意不要讓防火牆把關鍵的 WebFinger 服務給擋下來了。

備份與還原 Mastodon 資料

其實整個服務的設計與安裝都算是巧妙,到目前為止以及這樣正式營運一個社群媒體服務,資料通通都在當初 git clone 下來的 mastodon 目錄裡。

簡單來說,把整個目錄打包起來,就是從原始碼到使用者資料都一起了。

有網友分享的 Shell Script 如下:

set -e

NOW=$(date +%F)

# backup postgres
docker exec -it mastodon-db sh -c "pg_dumpall -U postgres > /tmp/all.sql"
docker exec -it mastodon-db sh -c "cp /tmp/all.sql /postgres_backups/all.sql"

# pack everything of value into zip
zip -r ./backup_$NOW.zip ./redis ./postgres_backups ./public ./backup.sh ./docker-compose.yml ./.env.production ./proxy -x "public/system/cache/**"

Ref: GitHub - shukriadams/mastodon-docker-simple-setup

還原則是把匯出的資料庫檔案重新匯入回去:

docker exec -it mastodon-db psql -U postgres --set ON_ERROR_STOP=off -f  /postgres_backups/all.sql

用容器開服務真的是方便R~

更新 Mastodon 版本

因為是用 Git 直接從原始碼的 Repo 抓下來,所以也不難:

  1. git fetch 指令將 repo 程式碼更新
  2. 輸入 git status 確認有動到的設定(可能是 docker-compose.yml 檔案)
  3. 有改動的話,先用 git stash 指令把修改的暫存起來。
  4. 使用 git checkout v版本號 前,確認是使用前面提到的 git stash 還是 git commit,如果是 stash 切換到最新版後使用 git stash pop 還原修改套用當前版本,如果是 commit 的話就要改用 merge 指令了。總而言之就是切換版本並保留原本客製化調整的部分。
  5. 由於有更新版本,官方提供的 docker-compose.yml 也會有變動版本,如果不是自己 build 映像檔的話,就是先 docker-compose pull,如果是自己有做調整,那就是需要重新 docker-compose build 過。
  6. 準備好映像檔 image 後,就是關停當前的容器, docker compose down,然後重新啟動 docker compose up -d。如果是使用官方 build 好的版本,直接關停後重啟也會自己 pull 過。
  7. (選擇性操作)使用 docker compose run --rm web rake db:migrate 確保有無資料庫的升級。
  8. (選擇性操作)使用 docker compose run --rm web rake assets:precompile 重新編譯過靜態相關文件。

Ref: McKael/mastodon-documentation/blob/master/Running-Mastodon/Docker-Guide.md

後記

其實看下來主體就是那包原始碼與容器映像檔的服務。其他都是網路服務的配置而已。

看起來 ActivityPub 協議 用在串聯彼此又不會失去內容與注意力主權,真的很棒啊~

就連 馬克·祖克柏都已經說社群媒體結束了

「在 Meta 的辯護聲明中,他們展示了一張圖表,顯示用戶觀看「朋友」所發內容的比例,在 Facebook 上從 22% 降至 17%,在 Instagram 上則從 11% 降至 7%。」

比起過時又長篇大論的 RSS Feed,那換一個輕鬆交流的方式吧!

WordPress 也有社群維護的 ActivityPub 外掛可以使用,讓你的網站也能在發佈內容後從像是 Mastodon 的服務來接收通知。

  • 驗證社群個人資訊頁面的工具: mastodon-link-debugger - 檢查分享出去對應的網站與個人資訊卡的設定是否正確。
  • 台灣 Mastodon 的相關資源: awesome-fediverse-in-taiwan - 要找到台灣同好,以及 Mastodon 中繼站可以到這邊看看~

Share:

作者: Chun

資訊愛好人士。主張「人人都該為了偷懶而進步」。期許自己成為斜槓到變進度條 100% 的年輕人。[///////////____36%_________]

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *


文章
Filter
Apply Filters
Mastodon