MAATRIX / Блог / Outline на сервере: частые ошибки и решения

Outline на сервере: частые ошибки и решения

MAATRIX

Outline — быстрая корпоративная вики с Markdown-редактором и хорошим поиском, живая альтернатива Notion для команд, которым нужен self-hosted вариант. Ставится она через Docker Compose быстро, но именно там, где документация экономит слова — авторизация, хранилище файлов, почта — и начинаются проблемы. Ниже — конкретные ошибки, с которыми реально сталкиваются при развёртывании Outline на своём сервере, и как их закрыть.

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

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

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

Контейнер стартует и сразу падает

Самая частая ситуация: docker compose up -d отрабатывает, но через несколько секунд контейнер outline уходит в Restarting. Смотрим логи:

docker compose logs outline --tail=100

Три типичные причины:

  1. Не сгенерирован SECRET_KEY / UTILS_SECRET. Outline требует два случайных hex-ключа по 32 байта каждый. Пустые или одинаковые значения — контейнер падает с ошибкой валидации на старте.
openssl rand -hex 32   # для SECRET_KEY
openssl rand -hex 32   # для UTILS_SECRET, значение должно отличаться
  1. Postgres ещё не готов, а Outline уже пытается подключиться. Docker Compose по умолчанию не ждёт готовности базы, только запуск контейнера. Решение — healthcheck и depends_on: condition: service_healthy:
services:
  postgres:
    image: postgres:16
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U outline"]
      interval: 5s
      timeout: 5s
      retries: 10
  outline:
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
  1. URL в .env не совпадает с реальным доменом (или указан с http вместо https, или с завершающим слэшем). Outline сверяет этот параметр при формировании cookie и redirect-адресов OAuth — расхождение ломает не запуск, а именно вход после логина.

OAuth не даёт войти: redirect_uri mismatch

Outline не имеет собственной формы логина «email + пароль» из коробки — только сторонние провайдеры (Google, Slack, Microsoft, GitHub через community-сборки или generic OIDC). Самая частая ошибка на этом этапе — Error 400: redirect_uri_mismatch от Google.

Причина почти всегда одна: в консоли провайдера указан один redirect URI, а Outline формирует другой. Проверьте три вещи одновременно:

  • URL в .env — это ровно тот домен, с которого открывается вики (https://wiki.example.com, без слэша в конце);
  • в Google Cloud Console → Credentials → OAuth 2.0 Client IDs redirect URI указан как https://wiki.example.com/auth/google.callback (для Slack — /auth/slack.callback, для generic OIDC — /auth/oidc.callback);
  • GOOGLE_CLIENT_ID и GOOGLE_CLIENT_SECRET (или аналоги для другого провайдера) не содержат случайных пробелов — при копировании из консоли в .env это встречается чаще, чем кажется.

Для generic OIDC (например, Keycloak, Authentik или Zitadel — актуальный вариант, если не хотите завязываться на Google) дополнительно нужны:

OIDC_CLIENT_ID=outline
OIDC_CLIENT_SECRET=...
OIDC_AUTH_URI=https://auth.example.com/application/o/authorize/
OIDC_TOKEN_URI=https://auth.example.com/application/o/token/
OIDC_USERINFO_URI=https://auth.example.com/application/o/userinfo/
OIDC_USERNAME_CLAIM=preferred_username
OIDC_DISPLAY_NAME=SSO
OIDC_SCOPES="openid profile email"

Если провайдер выдаёт токен, но Outline создаёт пользователя без имени или email — проверьте OIDC_USERNAME_CLAIM: часто в нём указывают sub (просто ID) вместо реального claim с именем.

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

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

Арендовать сервер

Загрузка файлов и картинок не работает (S3)

Outline с 2023 года требует S3-совместимое хранилище для вложений — локальную файловую систему как storage backend разработчики убрали из официальной сборки. Без корректного S3 вставка изображений в документ либо зависает на «Uploading…», либо падает с ошибкой доступа.

Минимальный рабочий набор переменных:

AWS_ACCESS_KEY_ID=your-key
AWS_SECRET_ACCESS_KEY=your-secret
AWS_REGION=us-east-1
AWS_S3_UPLOAD_BUCKET_URL=https://s3.example.com
AWS_S3_UPLOAD_BUCKET_NAME=outline-uploads
AWS_S3_FORCE_PATH_STYLE=true
AWS_S3_ACL=private

Если поднимаете собственный S3-совместимый сервис (MinIO — рабочий вариант, не нужно платить внешнему провайдеру за объектное хранилище), обратите внимание на три момента:

  • AWS_S3_FORCE_PATH_STYLE=true обязателен для MinIO — без него запросы уходят на несуществующий virtual-hosted домен вида bucket.s3.example.com;
  • бакет должен существовать заранее, Outline его не создаёт;
  • CORS на бакете должен разрешать origin вашего домена вики, иначе браузер молча блокирует загрузку скриншотов при вставке через буфер обмена.

Если MinIO крутится в соседнем контейнере на том же сервере — разбирали отдельно, что именно там ломается чаще всего, в статье про MinIO на сервере: частые ошибки и решения.

Письма не приходят: инвайты, сброс пароля, уведомления

Outline использует SMTP только для двух вещей: приглашения новых участников и (опционально) email-уведомлений. Без корректного SMTP не критично для работы вики, но команда не сможет пригласить коллег ссылкой на почту.

SMTP_HOST=smtp.yourmail.com
SMTP_PORT=587
SMTP_USERNAME=noreply@example.com
SMTP_PASSWORD=...
SMTP_FROM_EMAIL=noreply@example.com
SMTP_TLS_CIPHERS=
SMTP_SECURE=false

Частая ошибка — SMTP_SECURE=true при порте 587. Порт 587 — это STARTTLS, а не implicit TLS: там SMTP_SECURE должен быть false (шифрование включается по протоколу после подключения). Implicit TLS (SMTP_SECURE=true) — это порт 465. Перепутанная пара порт/флаг — самая типичная причина тихого падения писем без внятной ошибки в логах.

Если письма уходят, но попадают в спам — проверьте SPF/DKIM/DMARC для домена, с которого шлёте SMTP_FROM_EMAIL. Это отдельная настройка на уровне DNS, к Outline отношения не имеющая, но именно она чаще всего оказывается причиной «письмо не пришло» на практике.

Redis и очереди: медленный поиск, зависшие задачи

Outline держит в Redis очереди фоновых задач (индексация для поиска, экспорт документов, обработка вебхуков) и WebSocket-подписки для live-редактирования. Если Redis недоступен или падает, симптомы не всегда очевидны:

  • поиск по вики находит старые версии документов или не находит новых вообще — переиндексация не выполнилась;
  • совместное редактирование в реальном времени работает, но без индикатора «кто сейчас редактирует» — это тоже завязано на Redis pub/sub;
  • при удалении/восстановлении коллекций документы «зависают» в промежуточном статусе.

Проверка простая:

docker compose exec redis redis-cli ping
# должно вернуть PONG
docker compose logs outline | grep -i redis

Если Redis перезапускался без --appendonly yes и без volume под /data, очереди фоновых задач теряются при каждом рестарте контейнера — обязательно монтируйте persistent volume даже для инстанса Redis, который вы считаете «временным кэшем».

redis:
  image: redis:7-alpine
  command: redis-server --appendonly yes
  volumes:
    - redis-data:/data

Reverse proxy: 502, вебсокеты не подключаются, гигантские загрузки рвутся

Outline за Nginx или Traefik — стандартная схема, но есть три специфичных для него нюанса, которые не всплывают в общих гайдах по reverse-proxy.

WebSocket не проксируется. Совместное редактирование и live-обновления идут через WebSocket-соединение. Без явных заголовков Upgrade Nginx рвёт это соединение — редактор откатывается в режим без realtime-синхронизации, что выглядит как «баг», а не как проблема конфигурации:

location / {
    proxy_pass http://127.0.0.1:3000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    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_read_timeout 86400;
}

Последняя строка — proxy_read_timeout — важна отдельно: дефолтные 60 секунд Nginx рвут долгоживущие WebSocket-соединения, и пользователи видят разрывы связи каждую минуту при простое без активности.

Большие вложения падают с 413. Дефолтный лимит Nginx на тело запроса — 1 МБ, чего хватает на текст, но не на скриншот с ретина-дисплея или PDF-вложение:

client_max_body_size 50m;

Заголовок X-Forwarded-Proto не доходит до приложения — тогда Outline генерирует ссылки на http:// даже за HTTPS-терминацией, и браузер блокирует часть контента как mixed content. Проверьте, что заголовок выставлен именно на уровне location, а не только на уровне server — при нескольких вложенных proxy_pass (например, Traefik перед Nginx) он теряется на промежуточном хопе.

Общий разбор ошибок настройки Nginx как reverse-proxy — в статье Nginx как reverse-proxy на сервере: частые ошибки и решения, там про заголовки и таймауты подробнее вне контекста конкретно Outline.

База данных: миграции и бэкапы

Postgres для Outline — не «просто база», в неё пишутся не только документы, но и версии правок (revision history), поэтому она растёт быстрее, чем кажется на старте, особенно если команда активно правит документы.

Ошибка, которая всплывает при обновлении версии Outline — контейнер падает на старте с чем-то вроде relation "..." does not exist. Обычно это означает, что миграции не применились автоматически (при рестарте после docker compose pull образ меняется, а команда запуска — нет). Явный прогон миграций перед стартом:

docker compose run --rm outline yarn db:migrate

Бэкап делайте на уровне Postgres, а не файлов контейнера — pg_dump в связке с cron-задачей, вынесенной за пределы контейнера:

docker compose exec -T postgres pg_dump -U outline outline | gzip > outline_$(date +%F).sql.gz

Если база уже разрослась и хочется системного подхода к настройке самого Postgres под нагрузку, а не только к бэкапам — пригодится статья PostgreSQL на сервере: частые ошибки и решения.

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

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

Арендовать сервер

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

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

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

Можно ли запустить Outline без S3, чисто на локальном диске?

Официально — нет, с 2023 года код для локального file storage удалён из основной ветки. Обходной путь — поднять MinIO в соседнем контейнере на том же сервере, тогда S3 API есть, а данные физически остаются у вас.

Почему после логина через Google сразу выкидывает обратно на страницу входа?

Чаще всего — рассинхрон URL в .env с реальным доменом или неверный SECRET_KEY/UTILS_SECRET (если их поменяли после того, как пользователи уже залогинились, все существующие сессии и cookie становятся невалидными).

Сколько ресурсов реально нужно для команды из 20-30 человек?

Как ориентир — 2 vCPU и 4 ГБ RAM с запасом хватает на Outline + Postgres + Redis для такой команды при умеренной активности; точные цифры зависят от объёма вложений и частоты правок, стоит проверить на своей нагрузке.

Outline и Outline VPN — это одно и то же?

Нет, это два разных продукта с совпадающим названием: вики-система Outline от Wiki (Getoutline.com) и Outline VPN (Outline Server/Manager) от Jigsaw/Google — они не связаны и не имеют общего кода, легко перепутать при поиске документации.

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

Да — переносите Postgres (pg_dump/pg_restore) и содержимое S3-бакета целиком, историю ревизий это сохраняет полностью, поскольку она хранится в базе, а не в файлах.

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

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

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