YJK

独立世界

Independent World
twitter
telegram

Set up Mastodon and join the federated social network

Mastodon is a free, open-source social network server based on the ActivityPub protocol, where users can follow friends and discover new ones. On Mastodon, users can post any content, including links, images, text, and videos. All Mastodon servers can communicate with each other, forming a federated network, allowing seamless interaction with Mastodon users even when using non-Mastodon software that implements the ActivityPub protocol!

Mastodon is a decentralized social network, meaning each user can choose their own server (or "instance") and control their own data while still being part of the Mastodon user base. Because Mastodon is based on the ActivityPub protocol, it can also communicate with other decentralized social networks that use the same protocol, such as Pleroma, Friendica, and Hubzilla. This interoperability helps foster more diverse and resilient online communities, preventing users from being confined to a single centralized platform or company.

Here is a brief introduction on how to set up Mastodon on a VPS (recommended at least 1C2G, based on Debian 11 root user):

image

1/ Install Docker and Compose#

One command:

curl -L get.docker.com | bash

2/ Install Nginx and ACME.SH#

Here we recommend the Nginx packaged by n.wtf:

# 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

Install acme.sh with one command:

curl -L get.acme.sh | bash

After installation, you may need to close the current terminal and reopen it for the acme.sh alias to take effect.

3/ Install Mastodon#

Create a new folder

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

Edit docker-compose.yml

vim docker-compose.yml

Press i to start editing, press Shift+Ins to paste, press Esc to enter :wq to save and exit

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

Configure the database

docker run --name postgres12 -v /var/www/mastodon/postgres:/var/lib/postgresql/data -e   POSTGRES_PASSWORD=set database admin password --rm -d postgres:12.5-alpine

Check the postgres folder; multiple files related to postgres should appear, not an empty folder.

Then execute

docker exec -it postgres12 psql -U postgres

Enter

CREATE USER mastodon WITH PASSWORD 'database password (preferably different from the database admin password)' CREATEDB;

Create the mastodon user, then stop docker

docker stop postgres12

Configure .env.production

# Return to the mastodon folder
cd /var/www/mastodon

# Generate file
touch .env.production

# Configure file
docker compose run --rm web bundle exec rake mastodon:setup
  1. Enter the domain name
  2. Enable single user mode? No
  3. Using Docker to run Mastodon? Yes
  4. Fill in the postsql username and database name as mastodon, and enter the database password set earlier
  5. Press enter for all redis parts
  6. Store uploaded files on the cloud? This can be filled in as no, S3 can be configured if needed.
  7. Send e-mails from localhost? No. Then fill in the email service settings, it is recommended to use Mailazy, specific configuration can refer to below.
  8. This configuration will be written to .env.production Save configuration? Yes
  9. Then the .env.production configuration will appear, copy it down and save it to your computer for later use.
  10. Then it will ask you to create a database and compile, select yes for both. Finally, create the admin account.

After successful completion, edit the .env.production file, press i to start editing, press Shift+Ins to paste, press Esc to enter :wq to save and exit

vim .env.production

S3 and SMTP information is roughly as follows

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 # This configuration is only applicable to mailazy, please make corresponding changes for other service providers
SMTP_OPENSSL_VERIFY_MODE=none # This configuration is only applicable to mailazy, please make corresponding changes for other service providers
SMTP_FROM_ADDRESS=mastodon@[hidden]

Run

docker compose up -d

Grant permissions to the corresponding folders

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

# Stop
docker compose down

# Start again
docker compose up -d

Check the running details

docker compose ps
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

# When the status is all healthy, it means it is running successfully

4/ Configure Nginx and SSL#

Create a configuration file

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

Copy the following http configuration information to apply for the SSL certificate, press i to start editing, press Shift+Ins to paste, press Esc to enter :wq to save and exit

server {
  listen 80;
  # If IPV6 is configured, remove the hash below
  # 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; }
}

# Replace mastodon.im.sb with your own domain name

Reload Nginx

nginx -t
# Ensure the configuration file is error-free
# nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
# nginx: configuration file /etc/nginx/nginx.conf test is successful

# Reload
nginx -s reload

Apply for SSL certificate

Point your domain's A record and AAAA record (if any) to the VPS IP, then apply for the domain certificate

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

# Replace mastodon.im.sb with your own domain name

After successful application, we need the following:

# SSL full certificate
/root/.acme.sh/mastodon.im.sb_ecc/fullchain.cer
# SSL private key
/root/.acme.sh/mastodon.im.sb_ecc/mastodon.im.sb.key

Edit the configuration file again

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

Copy the following complete configuration information, press i to start editing, press Shift+Ins to paste, press Esc to enter :wq to save and exit

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;
  # If IPV6 is configured, remove the hash below
  # 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;
  # If IPV6 is configured, remove the hash below
  # 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;
}

# Replace mastodon.im.sb with your own domain name

Reload Nginx

nginx -t
# Ensure the configuration file is error-free
# nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
# nginx: configuration file /etc/nginx/nginx.conf test is successful

# Reload
nginx -s reload

Now you can access Mastodon by visiting your domain.

5/ Set Admin#

Visit your Mastodon and register an account, then return to the terminal and run:

# Return to the mastodon folder
cd /var/www/mastodon
docker compose run --rm web bin/tootctl accounts modify [YOURACCOUNT] --role Owner

Or create an admin account directly in the terminal:

docker compose run --rm web bin/tootctl accounts create  [YOURACCOUNT] --email [[email protected]] --confirmed --role Owner

A randomly generated password will be displayed in the terminal.


👉 Some content is referenced from this article.

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.