SelfHost
February 8

Mastodon + Docker + Portainer + Nginx. Открываем двери федивёрса на VPS Ubuntu 22.04

Mastodon - идеальный сервис для знакомства с федивёрс!
Mastodon — социальная сеть микроблогов, использующая в своей основе модель децентрализации, называемую также федерацией, федиверсом или федеративной вселенной реализованную с помощью протокола ActivityPub. Он имеет открытый исходный код и может быть развернут любым пользователем на любом сервере, причем каждый сервер является полностью самостоятельным экземпляром Mastodon. Каждый экземпляр может иметь свои собственные политики, правила модерации и управляется только Вами. В федерации нет корпоративных модераторов от компаний-разработчиков, управление всем контентом осуществляется только Вами и никто извне не сможет вас ограничить в рамках вашего экземпляра. В федерации, состоящей из всех пользовательских серверов Mastodon (и других сервисов, например Pixelfed, PeerTube и т. д.), каждый экземпляр может быть осведомлен о других экземплярах по протоколу ActivityPub. Это позволяет взаимодействовать с другими пользователями как внутри одного сервера, так и между разными экземплярами Mastodon.
Это руководство - перенос моей существующей статьи из self-host инстанса Ghost. На сегодняшний день последняя актуальная версия Mastodon 4.2.5. Однако, там фундаментально ничего не изменилось с 4.2.1 и все будет работать так, как описано в этом руководстве. В ближайшее время я постараюсь обновить это руководство, т.к. если с актуальностью технической части все хорошо, то некоторые логические последовательности стоит переписать для простоты понимания.

В этом руководстве вы установите последнюю версию Mastodon, которая на момент написания статьи является 4.2.1. Настроите свой сервер Ubuntu 22.04, установите Docker для работы с контейнерами, а так же настроите Portainer для управление Вашими Docker-контейнерами через графический интерфейс, а не командную строку. Также установите proxy-сервер nginx и получите для всех своих сервисов SSL/TLS Let's Encrypt сертификаты для защищенного HTTPS соединения.

Данное руководство можно считать расширенной версией большинства руководств. Я не буду рассказывать «как запустить инстанс за 2 минуты», чтобы потом удалить его через 20 минут, т. к. он совершенно не подходит для дальнейшего развития. Я расскажу как сделать правильно и визуально приятно глазу, чтобы вы сами видели результат своих трудов и могли смотреть логи и ошибки в графическом интерфейсе, а не в черном окошке. Поэтому тут будут моменты, когда было бы проще сделать «все в одной корзине» и Вы бы просто ноубрейн вставляли код, но я буду объяснять почему так и зачем. Чтобы в дальнейшем вы понимали что Вы делали и зачем это делали. Мы будем идти немного более сложным путем, чем могли бы, но это Вам пригодится потом, когда Вы, например, захотите вынести свою базу данных на отдельный VPS сервер и держать ее там для повышения производительности и безопасности. Опыт из данного руководства подготовит Вас к этому. Так что старт будет немного тернист, но перспективы прекрасны.

Что у Вас уже есть перед началом:

  • Арендованный у cloud-провайдера VPS сервер Ubuntu 22.04 (рекомендую Timeweb);
  • Ваш домен куплен и его DNS А-записи указывают на IPv4 адрес вашего VPS сервера (например example.com).
  • Создан поддомен для Portainer и А-записи поддомена также указывают на IPv4 адрес вашего VPS сервера (например panel.example.com).

Ну что же, начинаем!

Подготовка


Для начала установим весь необходимый софт для установки и дальнейшего обслуживания нашей ноды Mastodon:

sudo apt install wget curl nano software-properties-common dirmngr apt-transport-https gnupg gnupg2 ca-certificates lsb-release ubuntu-keyring unzip ufw htop -y

Зададим политики файрвола, чтобы контролировать порты на Вашем сервере:

sudo ufw allow OpenSSH
sudo ufw allow http
sudo ufw allow https

И включим файрвол:

sudo ufw enable

Это все, что необходимо сделать с сервером для стабильной работы. Дальше устанавливаем Docker, который в дальнейшем будет отвечать за контейнеры Mastodon.

Сначала убедимся, что у Вас нет никаких старых установок Docker и от него нет никакого мусора в системе, а если есть, то удалим это:

for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done

Обновляем систему:

sudo apt-get update

Добавляем APT репозиторий Docker на сервер:

sudo apt-get update
sudo apt-get install ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
echo \
  "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update

Теперь устанавливаем сам Docker:

sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Удостоверимся, что Docker работает правильно:

sudo docker run hello-world

Чтобы два раза не возвращаться к Docker, сразу установим к нему Portainer. Это делается в пару команд.

Для начала создадим docker-том, где будет работать Portainer:

docker volume create portainer_data

И установим сам Portainer:

docker run -d -p 8000:8000 -p 9443:9443 --name portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer-ce:latest

Все. Теперь, если Вы перейдете по вашему IP адресу сервера (например х.х.х.х:9443) или по адресу домена, который вы привязали к вашему серверу (например panel.example.com:9443), то вы попадаете в графический интерфейс панели управления Portainer, который в свою очередь управляет Docker установленный на вашем сервере. В дальнейшем Вы избавитесь от необходимости указывать порт 9443 для доступа к Portainer.

Установка Mastodon


Для начала Вам необходимо создать директории на нашем сервере, где Вы будете хранить ваши данные. Я предпочитаю все разделять по директориям, чтобы потом понимать где что искать.

Начнем с создания директорий для базы данных:

sudo mkdir -p /opt/database/mastodon/{postgresql,pgbackups,redis}

и сразу задаем права для директории:

sudo chown 70:70 /opt/database/mastodon/pgbackups

Точно так же создаем директорию для данных самого Mastodon:

sudo mkdir -p /opt/mastodon/web/{public,system}
sudo mkdir -p /opt/mastodon/branding

И задаем права для них:

sudo chown 991:991 /opt/mastodon/web/{public,system}

После этого идем в установленный ранее Portainer. При первом запуске он предложит вам зарегистрировать аккаунт администратора.

примечание:
при первом заходе в Portainer, возможно он Вас попросит перезагрузить свой контейнер и не даст создать аккаунт. В этом случае просто перезагрузите контейнер командой docker restart portainer_agent и зайдите в Portainer снова.

После входа в Portainer перейдите на вкладку Home и нажмите Live Connect к вашему единственному серверу. Дальше в левой части экрана у вас появится новое меню, где Вас интересует пункт Stack. Переходите в него и нажимайте +add stack.

Перед Вами откроется окно для создания файла docker-compose.yml, выглядит это так:

docker-compose.yml в Portainer

Здесь вводите название на Ваш выбор. Главное, чтобы Вы сами понимали что оно означает. Например mastodon_db, а в основное окно ниже вставляем следующий код:

version: '3'
services:

  postgres:
    image: postgres:15-alpine
    container_name: postgres
    shm_size: 512mb
    restart: always
    network_mode: bridge
    ports:
      - '5432:5432'
    healthcheck:
      test: ['CMD', 'pg_isready', '-U', 'postgres']
    volumes:
      - postgresql:/var/lib/postgresql/data
      - pgbackups:/backups
    environment:
      - 'POSTGRES_HOST_AUTH_METHOD=trust'

  redis:
    image: redis:7-alpine
    container_name: redis
    restart: always
    network_mode: bridge
    ports:
      - '6379:6379'
    healthcheck:
      test: ['CMD', 'redis-cli', 'ping']
    volumes:
      - redis:/data

  redis-volatile:
    image: redis:7-alpine
    container_name: redis-volatile
    restart: always
    network_mode: bridge
    healthcheck:
      test: ['CMD', 'redis-cli', 'ping']

volumes:
  postgresql:
    driver_opts:
      type: none
      device: /opt/database/mastodon/postgresql
      o: bind
  pgbackups:
    driver_opts:
      type: none
      device: /opt/database/mastodon/pgbackups
      o: bind
  redis:
    driver_opts:
      type: none
      device: /opt/database/mastodon/redis
      o: bind

После этого внизу экрана нажимаем Deploy the stack и ждем, когда появится уведомление о удачном развертываний Ваших баз данных.

Теперь точно так же делаем новый Stack, называем его как-то удобно для Вас, например mastodon_web и копируем следующий код:

version: '3'
services:

  website:
    image: ghcr.io/mastodon/mastodon:v4.2.1
    container_name: website
    env_file: stack.env
    command: bash -c "rm -f /mastodon/tmp/pids/server.pid; bundle exec rails s -p 3000"
    restart: always
    ports:
      - '127.0.0.1:3000:3000'
    networks:
      - internal_network
      - external_network
    healthcheck:
      test: ['CMD-SHELL', 'wget -q --spider --proxy=off localhost:3000/health || exit 1']
    volumes:
      - uploads:/mastodon/public/system

  streaming:
    image: ghcr.io/mastodon/mastodon:v4.2.1
    container_name: streaming
    env_file: stack.env
    command: node ./streaming
    restart: always
    ports:
      - '127.0.0.1:4000:4000'
    networks:
      - internal_network
      - external_network
    healthcheck:
      test: ['CMD-SHELL', 'wget -q --spider --proxy=off localhost:4000/api/v1/streaming/health || exit 1']

  sidekiq:
    image: ghcr.io/mastodon/mastodon:v4.2.1
    container_name: sidekiq
    env_file: stack.env
    command: bundle exec sidekiq
    restart: always
    depends_on:
      - website
    networks:
      - internal_network
      - external_network
    healthcheck:
      test: ['CMD-SHELL', "ps aux | grep '[s]idekiq\ 6' || false"]
    volumes:
      - uploads:/mastodon/public/system

  shell:
    image: ghcr.io/mastodon/mastodon:v4.2.1
    container_name: shell
    env_file: stack.env
    command: /bin/bash
    restart: "no"
    networks:
      - internal_network
      - external_network
    volumes:
      - uploads:/mastodon/public/system
      - static:/static

networks:
  external_network:
  internal_network:
    internal: true

volumes:
  uploads:
    driver_opts:
      type: none
      device: /opt/mastodon/web/system
      o: bind
  static:
    driver_opts:
      type: none
      device: /opt/mastodon/web/public
      o: bind

Ниже этого окна будет еще один пункт, где вам нужно нажать advanced mode:

Advanced mode для заполнения переменных в docker-compose

В открывшееся окно вставляем значение переменных окружения (.env), которыми будет пользоваться Ваш Mastodon:

LOCAL_DOMAIN=example.com
SINGLE_USER_MODE=false
SECRET_KEY_BASE=257935a5e78673fc79b8888888888bb8a9e58452aa8aab6896794b0e31d8b77cf8888888888a96818913f3213472bb857e3f888888888805da1daeb1e88b9b53
OTP_SECRET=03d99fe6cd8dd316bb962ec366888888888822fcc0b4a31795554b5cee1f5d685fd88888888885c96979ca4f8097851cf5ca898888888888dfcdaa6ddce2fae1
VAPID_PRIVATE_KEY=8hk8888888888RpDA8888888888ZWHm9KYKIpIrhMj0=
VAPID_PUBLIC_KEY=BPZJW5ieC8888888888Zo9uyp05qUn1tbi8888888888Dxa81dvabtLCxgLUg94fliD4UJyY8888888888885lFdE=
DB_HOST=postgres
DB_PORT=5432
DB_NAME=postgres
DB_USER=mastodon
DB_PASS=verysecretpass
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_PASSWORD=
SMTP_SERVER=smtp.timeweb.ru
SMTP_PORT=25
SMTP_LOGIN=admin@example.com
SMTP_PASSWORD=0088664422
SMTP_AUTH_METHOD=plain
SMTP_OPENSSL_VERIFY_MODE=none
SMTP_ENABLE_STARTTLS=auto
SMTP_FROM_ADDRESS=Mastodon <admin@example.com>
примечание:
Тут присутствует настройка отправки почты через SMTP. Настоятельно рекомендую это сделать. Получите почтовый ящик к своему домену там же, где вы купили домен. В примере выше указаны настройки для Timeweb, в данном случаем Вам нужно будет изменить только SMTP_LOGIN и SMTP_PASSWORD на свои и все. Так же нужно заменить SMTP_FROM_ADDRESS на что-то свое. SMTP_FROM_ADDRESS задает какой адрес электронной почты отправителя будут видеть Ваши пользователи, когда им будут приходить письма от Вас с уведомлениями или регистрацией.

После этого нажимаем Deploy the stack и ждем, когда все контейнеры загрузятся. Это может занять несколько минут.

примечание:
при попытке первого деплоя этого стака, возможно, спустя пару минут у Вас появится ошибка в верхнем правом углу о неудаче. Ничего страшного, можно сказать что так и должно быть. Нажимаем еще раз Deploy the stack и он запустится без ошибок, если вы все сделали до этого правильно.

На данном этапе у Вас есть 2 стака, в первом находится база данных, во втором сам Mastodon. Сейчас нужно будет объединить их в одну сеть, чтобы они друг друга видели и могла общаться между собой. Для этого во втором стаке с Mastodon поочередно заходите (просто нажимайте на название) в каждый из контейнеров website, streaming и sidekiq и в самом низу страницы добавляйте их в сеть базы данных выбрав нужную сеть нажатием на Select a network. Если вы стак с базой данных назвали как я предлагал выше, то нужная сеть будет называться mastodon_db_default. Каждый из контейнеров website, streaming и sidekiq должен быть сразу в двух сетях - в своей родной и сети от базы данных, в данном случае в mastodon_db_default.

Конфигурация контейнеров


В целом мы сделали уже самое основное и по большому счету нам осталось это между собой подружить и запустить в рабочем состоянии, т.к. сейчас это запущено, но ничего не работает. И начинаем с конфигурации базы данных:

sudo docker run --name postgres14 -v /opt/database/mastodon/postgresql:/var/lib/postgresql/data -e POSTGRES_PASSWORD=verysecretpass --rm -d postgres:14-alpine

Заходим в контейнер с базой данных:

sudo docker exec -it postgres14 psql -U postgres

и создает там пользователя:

CREATE USER mastodon WITH PASSWORD 'verysecretpass' CREATEDB;

Выходим из базы данных обратно:

exit

и останавливаем контейнер:

sudo docker stop postgres14

На этом наша база данных настроена и готова в работе. Осталось закончить с конфигурацией контейнера Mastodon, но тут совсем просто, т.к. он будет просить Вас ввести те значения переменных (.env), которые вы указывали выше во время запуска второго стака. Например первое, что он попросит Вас указать - это адрес вашего домена example.com. В самом конце Вам будет предложено проверить правильность настройки SMTP и попросит ввести e-mail адрес куда отправить тестовое письмо. Сделайте это, чтобы быть уверенным, что все работает исправно. Когда будут введены все переменные из таблицы выше (то, что вы указывали в advanced mode в Portainer), соглашайтесь на предпоследний вопрос о подготовке базы данных и он Вам выдаст новый список переменных (для поля advanced mode в Portainer). Дальше будет предложено создать аккаунт администратора, от этого откажитесь.

Начинаем конфигурацию Mastodon:

sudo docker exec -it streaming bundle exec rake mastodon:setup

Скопируйте список переменных из терминала, вернитесь в Portainer во второй стак mastodon_web и вставьте теперь уже правильные переменные в advanced mode. После этого нажмите внизу страницы update the stack.

После того, как контейнеры будут перезагружены, убедитесь, что все 3 контейнера website, streaming и sidekiq находятся в сети mastodon_db_default, как мы это делали ранее заходя в каждый из них по очереди, если нет, то добавьте их снова в сеть.

Теперь нам осталось только создать администратора и на этом конфигурация самого Mastodon будет окончена. Для этого используйте пару следующих команд.

Заходим в контейнер Mastodon:

sudo docker exec -it streaming /bin/bash

Создаем аккаунт администратора:

RAILS_ENV=production bin/tootctl accounts create admin --email admin@example.com --role=Owner

Можете заменить admin на свой ник и почту тоже заменить на свою.

примечание:
Обратите внимание, что после выполнения команды выше, в окне терминала будет указан пароль от этого аккаунта. Запишите его сразу куда-то, чтобы не потерять.

Сразу подтверждаем свой аккаунт, без необходимости делать это через почту:

RAILS_ENV=production bin/tootctl accounts modify admin --confirm

Замените admin на Ваш ник, если вы его заменили в прошлой команде.

Выходим из контейнера:

exit

На этом экземпляр Mastodon готов и полностью готов к работе. Осталось дело за малым - выпустить SSL сертификаты и настроить nginx.

Установка NGINX


Импортируем ключи nginx:

curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor \
	| sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null

Добавляем репозиторий стабильной версии nginx:

echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg arch=amd64] \
http://nginx.org/packages/ubuntu `lsb_release -cs` nginx" \
    | sudo tee /etc/apt/sources.list.d/nginx.list

Обновляем систему:

sudo apt update

И устанавливаем nginx:

sudo apt install nginx

Запускаем сервис nginx:

sudo systemctl start nginx

Получаем SSL-сертификаты


Для получения сертификатов нам нужен Certbot. Есть разные версии, но мы будем использовать версию последнюю версию в Snapd:

sudo snap install core
sudo snap refresh core

Устанавливаем Certbot:

sudo snap install --classic certbot

Для исправной работы Certbot создаем символическую ссылку:

sudo ln -s /snap/bin/certbot /usr/bin/certbot

Если вы правильно настроили свои DNS A-записи для своего домена, то следующая команда сгенерирует Вам SSL сертификаты:

sudo certbot certonly --nginx --agree-tos --no-eff-email --staple-ocsp --preferred-challenges http -m name@example.com -d example.com
Замените почту и адрес домена example.com на свои.

Так же сразу получите сертификаты для своего поддомена для Portainer, заменив example.com на panel.example.com.

Получаем сертификат Diffie-Hellman:

sudo openssl dhparam -dsaparam -out /etc/ssl/certs/dhparam.pem 4096

Убедитесь, что обновление сертификатов работает исправно, выполнив следующую команду:

sudo certbot renew --dry-run

Настраиваем NGINX


Открываем файлик конфига с помощью редактора nano:

sudo nano /etc/nginx/nginx.conf

В открывшемся файле найдите строку include /etc/nginx/conf.d/*.conf; и перед этой строкой вставьте следующую строку:

server_names_hash_bucket_size 64;

Нажмите Ctrl + S, чтобы сохранить изменения и закройте редактор нажатием Ctrl + X.

Создайте и откройте в редакторе файл конфигурации Nginx:

sudo nano /etc/nginx/conf.d/mastodon.conf

и вставьте туда следующий код:

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 example.com;
  root /opt/mastodon/web/public;
  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 example.com;

  ssl_protocols TLSv1.2 TLSv1.3;

  # You can use https://ssl-config.mozilla.org/ to generate your cipher set.
  # We recommend their "Intermediate" level.
  ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305;

  ssl_prefer_server_ciphers on;
  ssl_session_cache shared:SSL:10m;
  ssl_session_tickets off;

  # Uncomment these lines once you acquire a certificate:
   ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
   ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

  keepalive_timeout    70;
  sendfile             on;
  client_max_body_size 99m;

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

  location / {
    try_files $uri @proxy;
  }

  # If Docker is used for deployment and Rails serves static files,
  # then needed must replace line `try_files $uri =404;` with `try_files $uri @proxy;`.
  location = /sw.js {
    add_header Cache-Control "public, max-age=604800, must-revalidate";
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
    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;
}

В этом коде есть 4 места, где нужно заменить example.com на ваш адрес домена Mastodon.

И сразу создайте файл конфига Nginx для Portainer:

sudo nano /etc/nginx/conf.d/portainer.conf

И вставьте туда следующий код:

upstream portainer {
    server 0.0.0.0:9443;
}

server {
    listen 80;
    server_name panel.example.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl;

    # The block below works only for the selected domains
    server_name panel.example.com;

    ssl_certificate /etc/letsencrypt/live/panel.example.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/panel.example.com/privkey.pem; # managed by Certbot
    ssl_trusted_certificate /etc/letsencrypt/live/panel.example.com/chain.pem;

    access_log /var/log/nginx/portainer.access.log;
    error_log /var/log/nginx/portainer.error.log;

    location / {
        proxy_pass https://portainer/;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
        proxy_set_header Host $host;
    }

    location /api/websocket/ {
        proxy_pass https://portainer/api/websocket/;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
        proxy_set_header Host $host;
    }
}

В этом коде так же есть 4 места, где нужно заменить panel.example.com на ваш адрес поддомена Portainer.

Теперь перезапустим Nginx командой:

sudo systemctl restart nginx

Если все работает исправно, что никакого вывода в терминале не будет. Вы перезапустите Nginx и все.

Можете проверить синтаксис файлов конфигов Nginx следующей командой:

sudo nginx -t

Теперь, если вы перейдете по адресу своего домена example.com, то у вас будет открываться Ваш экземпляр Mastodon.

А переход по адресу panel.example.com будет открывать Ваш Portainer.

На этом все. У вас есть возможность отслеживать логи своего инстанса Mastodon, его поведение, обновление, поддержка и много другой работы через графический интерфейс на вашем домене Portainer, без необходимости постоянно открывать терминал и подключаться к VPS по SSH. Теперь дело за Вами, как Вы будете это развивать и администрировать. Удачи Вам на просторах федиверса!

Mastodon во всей красе!

Послесловие


Этим руководством я решил открыть свой маленький блог, где я планирую писать различные руководства для open-source софта, self-host ПО, Linux и т.д.