MAATRIX / Блог / Свой Mastodon: инстанс на одного и модерация, которой вы не ждали

Свой Mastodon: инстанс на одного и модерация, которой вы не ждали

MAATRIX

«Хочу свою альтернативу Twitter, только без рекламы и с полным контролем» — с этой мысли обычно начинается разговор про собственный инстанс Mastodon. И на первый взгляд всё выглядит как обычный self-hosted проект: скачал docker-compose, поднял, вбил домен, готово. На практике вы разворачиваете не блог-движок, а узел федеративной сети — со своей базой данных, очередями фоновых задач и, что удивляет почти всех, модерационной нагрузкой, которая начинается в день установки независимо от того, открыта у вас регистрация или нет. Разберём честно, что стоит за словом «instance», сколько инфраструктуры прячется за одной лентой и когда игра действительно стоит свеч.

Что вы получаете, а что перестаёте получать

Плюсы собственного инстанса реальны, и их стоит назвать прямо:

  • Идентичность вида @vy@vash-domen.ru — она принадлежит вам, а не платформе. Если крупный инстанс завтра сменит владельца или отключится, ваш аккаунт не исчезает вместе с ним.
  • Свои правила модерации. Никто не решает за вас, что можно постить и кого банить.
  • Контроль над федерацией. Вы сами решаете, с какими инстансами дружить, какие ограничивать (silence), а какие блокировать полностью (suspend/defederate).
  • Отсутствие алгоритмической ленты и рекламы — лента строго хронологическая, показывает то, на что вы подписаны.

Но вместе с этим вы забираете себе и то, что раньше делал за вас крупный инстанс незаметно:

  • Патчи безопасности и обновления Rails-приложения — накатывать нужно самому и не откладывая: Mastodon принимает трафик от тысяч чужих серверов.
  • Бэкапы базы данных и медиатеки — без них потеря сервера означает потерю не только постов, но и истории подписок.
  • Ответственность за контент, физически лежащий на вашем диске — включая то, что вы не публиковали сами (об этом ниже).
  • Модерационную очередь — да, даже у инстанса на одного человека.

«Инстанс на одного» (single-user instance) — легитимная и популярная практика именно ради контроля над идентичностью и правилами. Но она не освобождает от инфраструктурной и модерационной работы ниже — просто эта работа ложится на одного человека без команды, которая обычно есть у публичных инстансов.

Инфраструктура: сколько сервисов скрывается за одной лентой

Официальный образ Mastodon — это не один контейнер, а связка из нескольких сервисов, каждый со своей ролью:

КомпонентРоль
PostgreSQLОсновная база: аккаунты, посты, подписки, уведомления, статусы федерации
RedisКэш, очередь заданий для Sidekiq, pub/sub для live-обновлений через ActionCable
SidekiqФоновый воркер на Ruby: доставка и приём федеративных сообщений, рассылка писем, обработка медиа, плановые задачи
Puma (web)Rails-приложение — отдаёт веб-интерфейс и REST/Streaming API
StreamingОтдельный процесс для WebSocket-обновлений ленты в реальном времени
nginxОбратный прокси, TLS-терминация, лимиты на размер загружаемых файлов, кэш статики
Хранилище медиаЛокальный диск по умолчанию либо S3-совместимое хранилище
Elasticsearch/OpenSearch (опционально)Полнотекстовый поиск по чужим постам — без него поиск ограничен вашими постами и точным совпадением тегов

Даже минимальный self-hosted docker-compose для персонального инстанса — это не «один сервис», а минимум пять контейнеров, которые должны договориться друг с другом:

services:
  db:
    image: postgres:16-alpine
    restart: always
    volumes:
      - ./postgres:/var/lib/postgresql/data
    environment:
      POSTGRES_DB: mastodon
      POSTGRES_USER: mastodon

  redis:
    image: redis:7-alpine
    restart: always
    volumes:
      - ./redis:/data

  web:
    image: tootsuite/mastodon:latest
    restart: always
    env_file: .env.production
    command: bash -c "rm -f /mastodon/tmp/pids/server.pid; bundle exec rails s -p 3000"
    depends_on: [db, redis]
    volumes:
      - ./public/system:/mastodon/public/system

  streaming:
    image: tootsuite/mastodon:latest
    restart: always
    env_file: .env.production
    command: node ./streaming/index.js
    depends_on: [db, redis]

  sidekiq:
    image: tootsuite/mastodon:latest
    restart: always
    env_file: .env.production
    command: bundle exec sidekiq
    depends_on: [db, redis]
    volumes:
      - ./public/system:/mastodon/public/system

Обратите внимание: web, streaming и sidekiq — один и тот же образ, запущенный в трёх разных ролях. «Поднять Mastodon» с точки зрения ресурсов сервера — значит поднять три отдельных Ruby/Node-процесса плюс СУБД плюс Redis одновременно, а не по очереди. nginx перед этим стеком обязателен, а не опция: без client_max_body_size под видео и вложения и без правильного проксирования WebSocket на streaming-сервис лента у пользователей просто не обновляется в реальном времени.

server {
    listen 443 ssl http2;
    server_name mastodon.example.com;

    client_max_body_size 99M;

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

    location /api/v1/streaming {
        proxy_pass http://127.0.0.1:4000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_read_timeout 7d;
    }
}

Подробный разбор настройки nginx как обратного прокси со всеми заголовками и таймаутами — в статье про nginx как reverse proxy. PostgreSQL и Redis для Mastodon разворачиваются так же, как для любого Rails-приложения — базовая установка описана в статье про PostgreSQL на Ubuntu 24.04 и в статье про установку Redis на VPS, разница только в параметрах подключения из .env.production.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Развернуть Mastodon на VPS

Модерация, которую вы не выбирали: федерация не спрашивает разрешения

Вот главное, о чём не пишут в текстах про «свою альтернативу Twitter»: закрытая регистрация останавливает только один канал нагрузки — локальные спам-аккаунты. Всё остальное приходит независимо от того, кто может у вас зарегистрироваться.

Как именно это работает:

  • Вы подписываетесь на кого-то с другого инстанса — и их посты, репосты, ответы и медиавложения начинают приходить на ваш сервер через ActivityPub и складываться в вашу базу и на диск, независимо от числа ваших пользователей.
  • Кто угодно с любого инстанса может упомянуть или ответить вашему пользователю — это входящий федеративный трафик, который сервер обязан принять и обработать.
  • Жалобы (Flag-активности) на контент, видимый на вашем инстансе, приходят в вашу очередь модерации — даже если автор поста зарегистрирован в другом месте: копия объекта у вас в базе, и репорт адресован именно вашему серверу.
  • Медиавложения из чужих постов физически кэшируются на диске — аватары, обложки, картинки и видео всех, на кого вы подписаны или чьи посты попали в ленту через буст. Ответственность за то, что лежит в public/system, — ваша, даже если контент создан не вами.

Практический вывод: даже инстанс на одного человека с закрытой регистрацией нуждается в ком-то, кто время от времени открывает /admin/reports и решает, что делать с жалобой на пост третьего инстанса, о существовании которого вы вчера не подозревали. В одиночку это не критично при небольшом числе подписок, но это принципиально другая модель нагрузки, чем «поставил и забыл».

Отдельная задача — списки блокировки доменов (domain blocks). Готового провалидированного списка «спам-инстансов» Mastodon из коробки не поставляет — сообщество ведёт открытые списки (например, курируемые проектами вроде IFTAS), и их нужно самостоятельно импортировать через admin/instances или tootctl domains. Без этого шага на маленький инстанс натекает спам, который крупные инстансы давно отфильтровали себе.

Хранилище: как растут медиа даже у одного пользователя

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

Проверить, сколько места это занимает:

RAILS_ENV=production bin/tootctl media usage

Очистить кэш удалённых медиа старше определённого срока (при следующем обращении Mastodon перекачает их заново по требованию):

RAILS_ENV=production bin/tootctl media remove --days=30
RAILS_ENV=production bin/tootctl media remove-orphans
RAILS_ENV=production bin/tootctl accounts cull

Эти команды стоит вынести в cron — вручную про них вспоминают обычно уже после того, как диск закончился:

0 4 * * * cd /home/mastodon/live && RAILS_ENV=production bin/tootctl media remove --days=30 >> /var/log/mastodon-media-cleanup.log 2>&1

Если инстанс планируется не как разовый эксперимент, а как постоянная точка присутствия, разумнее с самого начала вынести медиахранилище на S3-совместимое хранилище, а не на локальный диск сервера — это снимает вопрос «докупать диск» и упрощает перенос инстанса на другой сервер в будущем. В .env.production это выглядит так:

S3_ENABLED=true
S3_BUCKET=mastodon-media
S3_REGION=us-east-1
S3_ENDPOINT=https://s3.example.com
AWS_ACCESS_KEY_ID=...
AWS_SECRET_ACCESS_KEY=...
S3_ALIAS_HOST=media.mastodon.example.com

Разворачивать своё S3-совместимое хранилище под такие задачи мы разбирали в статье про S3-совместимое хранилище у себя — тот же подход применим и к медиатеке Mastodon.

Sidekiq-очереди: что будет, если сервер не успевает

Всё взаимодействие с федерацией — отправка ваших постов подписчикам на других инстансах, приём входящих постов, рассылка уведомлений, генерация превью — проходит через фоновые задачи Sidekiq, разложенные по нескольким очередям: default, push (исходящая доставка), pull (входящие обновления и поиск профилей), mailers, scheduler, ingress (приём входящей федерации).

Конфигурация конкурентности задаётся в config/sidekiq.yml:

:concurrency: 25
:queues:
  - [ingress, 8]
  - [push, 6]
  - [pull, 4]
  - [default, 4]
  - [mailers, 2]
  - [scheduler, 2]

Если очередь ingress недоукомплектована воркерами относительно входящего потока, происходит не «сайт тормозит» в привычном понимании, а более тонкая проблема: удалённые инстансы, пытающиеся доставить вам сообщение, получают таймауты и уходят в повторные попытки по экспоненциальной задержке. Со стороны ваш сервер в этот момент выглядит нестабильным узлом федерации, и некоторые крупные инстансы автоматически понижают приоритет таких узлов.

Диагностировать отставание можно через встроенный веб-интерфейс Sidekiq (обычно смонтированный на /sidekiq) — там видна глубина каждой очереди и число повторных попыток. Растущая глубина очереди push/pull без роста числа пользователей — признак нехватки выделенных ядер CPU, а не проблема конфигурации как таковой.

Точную цифру по RAM и CPU для персонального инстанса честно назвать нельзя — она зависит не от числа ваших пользователей, а от числа аккаунтов, на которые вы подписаны, и от активности тех инстансов, с которыми вы федеритесь (это определяет объём фан-аута). Ориентируйтесь на то, что стек Postgres + Redis + Puma + Sidekiq + streaming требует заметно больше ресурсов, чем статический сайт той же посещаемости, и закладывайте запас сверх минимальных требований из документации — обработка медиа (превью, перекодирование видео через ffmpeg) даёт заметные пики нагрузки на CPU.

Когда свой инстанс оправдан, а когда лучше присоединиться к существующему

Свой инстанс имеет смысл, если для вас важно хотя бы одно из следующего:

  • Идентичность на собственном домене, не зависящая от политики чужого администратора.
  • Полный контроль над правилами модерации и списком заблокированных доменов под ваши задачи.
  • Интеграция с другими self-hosted сервисами (публикация из CI/CD, ботов, уведомлений).
  • Интерес к инфраструктуре как таковой — рабочий способ разобраться в Rails, ActivityPub и очередях на практике.

Присоединиться к существующему инстансу разумнее, если:

  • Вы не готовы дежурить по обновлениям безопасности и очередям Sidekiq на постоянной основе.
  • Вам важна модерационная команда, уже поддерживающая блок-листы и разбирающая репорты — в одиночку это всё на вас.
  • Сетевой эффект важнее контроля: на активном инстансе выше шанс, что вас увидят.
  • Доступность аккаунта не должна зависеть от того, продлили ли вы вовремя домен или сертификат.

Отдельно стоит развести Mastodon и другое федеративное ПО, которое путают именно из-за общего слова «fediverse». Mastodon — лента коротких постов, аналог Twitter/X: подписки, буст, лайк, хронологическая лента. Lemmy — форум-агрегатор ссылок с обсуждениями, аналог Reddit: сообщества, посты с комментариями, голосование. Оба протокола (ActivityPub) роднит федерация и, как следствие, схожая инфраструктурная и модерационная нагрузка — входящие репорты, кэш чужого контента, свои блок-листы. Но модель контента разная: в Mastodon вы модерируете ленту и упоминания, в Lemmy — сообщества и ветки обсуждений. Выбирая между ними, стоит для начала определить, нужна ли вам лента коротких сообщений или форум с обсуждениями — это разные продукты с общим только транспортным протоколом.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Развернуть Mastodon на VPS

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Нужен ли Elasticsearch для маленького личного инстанса?

Не обязательно. Базовый поиск по вашим собственным постам и точным совпадениям хештегов работает и без него. Elasticsearch/OpenSearch добавляет полнотекстовый поиск по чужим постам в вашей ленте — для инстанса на 1-5 человек это часто избыточно на старте, можно подключить позже.

Останавливает ли закрытая регистрация модерационную нагрузку?

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

Можно ли ограничить федерацию только доверенными инстансами?

Да, в Mastodon есть режим ограниченной федерации (allow-list вместо блок-листа) — данные идут только с явно разрешёнными доменами. Это снимает часть неожиданной нагрузки, но превращает публичную сеть в частную и лишает смысла часть преимуществ федерации.

Сколько ресурсов сервера реально нужно для инстанса на 1-3 человека?

Точную цифру честно назвать нельзя — она зависит от числа аккаунтов, на которые вы подписаны, а не от числа локальных пользователей. Закладывайте запас сверх минимальных требований из документации, особенно по CPU.

Чем это отличается от Lemmy, если оба — fediverse?

Mastodon — лента коротких постов, аналог Twitter/X. Lemmy — форум-агрегатор ссылок с обсуждениями, аналог Reddit. Общая федерация через ActivityPub даёт похожий профиль нагрузки, но модель контента разная.

Можно ли позже перенести инстанс на другой сервер?

Да, перенос базы PostgreSQL, Redis и медиахранилища технически несложен и обычно описан в официальной документации проекта. Но идентичность аккаунтов жёстко привязана к домену — смена домена ломает существующие подписки федерации, поэтому домен стоит выбирать один раз и надолго.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →