Outline в Docker Compose: готовый файл
Команда растёт, и заметки в личных Google Docs, скриншоты в чатах и файлы «финал_v3.docx» перестают работать как база знаний — искать в них невозможно, а доступы никто не помнит. Notion решает это удобно, но данные лежат на чужих серверах, а цена за место растёт с числом человек в команде. Outline — self-hosted вики с Markdown-редактором, быстрым полнотекстовым поиском и структурой из коллекций и документов: ниже рабочий docker-compose.yml, чтобы поднять её на своём сервере за один вечер.
Уточним сразу: речь о вики-движке getoutline.com, а не об Outline VPN от Jigsaw — это два разных продукта с одинаковым названием, ничего общего в коде и назначении у них нет.
Содержание
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Чем Outline отличается от Notion и когда он оправдан
Outline — это не клон Notion, а более узкий инструмент: документы, коллекции (папки верхнего уровня), вложенные страницы, комментарии и полнотекстовый поиск по всей базе. Нет баз данных со связями, канбан-досок и формул — если команде нужен именно такой конструктор, лучше смотреть в сторону NocoDB или Baserow. Зато у Outline быстрый и предсказуемый Markdown-редактор, который не тормозит на длинных документах, и API, которым удобно наполнять базу из скриптов.
Практические причины ставить self-hosted вместо облачного Notion:
- Данные остаются на вашем сервере. Для команды с внутренней документацией, доступами и паролями это не абстрактный принцип, а требование безопасности.
- Стоимость не растёт с числом пользователей. В облачных тарифах цена почти всегда за место; на своём сервере лимит — это диск и RAM, а не число сотрудников.
- Единая авторизация. Outline подключается к корпоративному OIDC/Google/Slack-логину — не нужно заводить отдельный пароль для базы знаний.
Честный минус: разворачивать и поддерживать стек — ваша ответственность, включая бэкапы и обновления. Если это лишняя головная боль, облачный тариф Outline или Notion объективно проще. Дальше — про то, как эту ответственность свести к минимуму.
Из чего состоит стек
Outline — не один контейнер, а связка из нескольких сервисов, и это стоит понимать до старта:
| Компонент | Роль | Обязателен? |
|---|---|---|
outline | само приложение (Node.js) | да |
| PostgreSQL 12+ | основная база: документы, права, история правок | да |
| Redis 6+ | очереди задач, сессии, pub/sub для совместного редактирования | да |
| Хранилище файлов (локальный диск или S3-совместимое) | вложения, картинки, экспорт | да, один из двух вариантов |
| SMTP-сервер | письма о упоминаниях, экспорт, магическая ссылка входа | нет, но без него часть уведомлений не работает |
Локальное хранение файлов проще для старта — Outline пишет вложения в примонтированный том. Но если вы уже держите S3-совместимое хранилище для бэкапов или других сервисов, разумнее сразу подключить его через переменные AWS_* — ниже покажу оба варианта. Развернуть свой S3-эндпоинт можно тем же вечером: у нас есть отдельный готовый compose-файл для MinIO.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверГотовый docker-compose.yml
Рабочий конфиг с Postgres, Redis и локальным хранением файлов — для старта этого достаточно:
services:
outline:
image: docker.getoutline.com/outlinewiki/outline:latest
container_name: outline
restart: unless-stopped
env_file: ./.env
ports:
- "127.0.0.1:3000:3000"
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
volumes:
- outline_data:/var/lib/outline/data
redis:
image: redis:7-alpine
container_name: outline_redis
restart: unless-stopped
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
postgres:
image: postgres:16-alpine
container_name: outline_postgres
restart: unless-stopped
environment:
POSTGRES_USER: outline
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: outline
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U outline"]
interval: 10s
timeout: 5s
retries: 5
volumes:
outline_data:
redis_data:
postgres_data:
Порт приложения намеренно привязан к 127.0.0.1:3000 — наружу его отдаёт reverse proxy по HTTPS, напрямую в интернет контейнер не смотрит. Официальный образ публикуется в реестре docker.getoutline.com — перед боевым запуском откройте страницу релизов на GitHub и замените latest на конкретный тег: так обновления происходят осознанно, а не при случайном docker compose pull.
Файл .env: обязательные переменные
Создайте .env рядом с docker-compose.yml. Два секрета сгенерируйте сами:
openssl rand -hex 32 # для SECRET_KEY
openssl rand -hex 32 # для UTILS_SECRET
Минимальный рабочий набор:
NODE_ENV=production
SECRET_KEY=<результат первой команды>
UTILS_SECRET=<результат второй команды>
URL=https://wiki.example.com
PORT=3000
FORCE_HTTPS=true
DATABASE_URL=postgres://outline:${POSTGRES_PASSWORD}@postgres:5432/outline
DATABASE_URL_TEST=postgres://outline:${POSTGRES_PASSWORD}@postgres:5432/outline-test
PGSSLMODE=disable
REDIS_URL=redis://redis:6379
POSTGRES_PASSWORD=<длинный случайный пароль>
FILE_STORAGE=local
FILE_STORAGE_LOCAL_ROOT_DIR=/var/lib/outline/data
FILE_STORAGE_UPLOAD_MAX_SIZE=26214400
Для S3-совместимого хранилища (например, свой MinIO) замените блок FILE_STORAGE:
FILE_STORAGE=s3
AWS_ACCESS_KEY_ID=<ключ>
AWS_SECRET_ACCESS_KEY=<секрет>
AWS_REGION=us-east-1
AWS_S3_UPLOAD_BUCKET_URL=https://s3.example.com
AWS_S3_UPLOAD_BUCKET_NAME=outline
AWS_S3_FORCE_PATH_STYLE=true
AWS_S3_ACL=private
AWS_S3_FORCE_PATH_STYLE=true обязателен для MinIO и большинства не-AWS хранилищ — без этого запросы уходят на несуществующий поддомен вида bucket.s3.example.com, которого у вас нет.
Авторизация: без неё Outline не запустится
Отличие от типичного self-hosted сервиса: Outline требует хотя бы один настроенный провайдер входа ещё до первого запуска — просто логина и пароля из коробки нет. Варианты, которые можно комбинировать:
- Google Workspace —
GOOGLE_CLIENT_ID,GOOGLE_CLIENT_SECRET, вход ограничивается доменом рабочей почты. - Slack —
SLACK_CLIENT_ID,SLACK_CLIENT_SECRET, удобно, если команда уже живёт в Slack. - Microsoft/Azure AD —
AZURE_CLIENT_ID,AZURE_CLIENT_SECRET,AZURE_RESOURCE_APP_ID. - Универсальный OIDC —
OIDC_CLIENT_ID,OIDC_CLIENT_SECRET,OIDC_AUTH_URI,OIDC_TOKEN_URI,OIDC_USERINFO_URI— подходит для своего Keycloak, Authentik или другого IdP, если хочется полностью независимую от больших платформ авторизацию.
Каждый провайдер требует зарегистрировать OAuth-приложение на стороне Google/Slack/Microsoft/вашего IdP с redirect_uri вида https://wiki.example.com/auth/google.callback (для остальных провайдеров — по аналогии, меняется только последний сегмент). Без правильного URL в .env редирект после входа не сработает — переменная должна точно совпадать с доменом, по которому открывается вики.
Reverse proxy, HTTPS и бэкапы
Outline использует WebSocket для совместного редактирования документов в реальном времени — в конфиге nginx это нужно явно учесть:
server {
listen 443 ssl http2;
server_name wiki.example.com;
ssl_certificate /etc/letsencrypt/live/wiki.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/wiki.example.com/privkey.pem;
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 https;
}
}
Про сам процесс настройки nginx как обратного прокси и получение сертификата Let's Encrypt — в отдельных пошаговых статьях: nginx как reverse proxy и общая база по Docker Compose для продакшена, если стек ставится с нуля.
Бэкап — это два независимых объекта: база данных и файлы вложений.
# дамп базы
docker exec outline_postgres pg_dump -U outline outline | gzip > outline_$(date +%F).sql.gz
# том с вложениями (при локальном хранении)
docker run --rm -v outline_outline_data:/data -v $(pwd):/backup alpine \
tar czf /backup/outline_files_$(date +%F).tar.gz -C /data .
Оба архива стоит выгружать за пределы сервера — вручную по расписанию через cron или инструментом вроде Restic, который умеет шифровать и складывать бэкапы прямо в S3-хранилище. Если вложения уже лежат в S3 (MinIO или облачный), то бэкапить их отдельно не нужно — достаточно бэкапа самого бакета и базы данных.
Обновление — стандартная процедура для стека на Docker Compose: смените тег образа в docker-compose.yml, снимите бэкап базы и выполните docker compose pull && docker compose up -d. Миграции базы данных Outline применяет автоматически при старте контейнера.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Outline бесплатен для self-hosted?
Да, код открыт (BSL с переходом в Apache 2.0 через несколько лет после релиза версии), self-hosted развёртывание бесплатно для собственного использования. Ограничения лицензии касаются перепродажи Outline как сервиса — уточните текущие условия в репозитории проекта перед коммерческим использованием.
Можно запустить без домена, просто по IP?
Технически да, но URL в .env должен точно совпадать с адресом, по которому открывается сайт, а часть провайдеров авторизации (например, Google) требует HTTPS и реального домена для redirect_uri — без домена рабочей останется разве что связка с локальным OIDC.
Сколько RAM нужно на старте?
Для команды до 20-30 человек хватает 2 ГБ RAM на весь стек (Outline + Postgres + Redis) с запасом; точная цифра зависит от объёма базы и параллельных редакторов, так что относитесь к этому как к ориентиру, а не гарантии.
Есть ли миграция из Notion?
Outline умеет импортировать экспорт Notion в формате Markdown/HTML через раздел настроек «Import» — структура коллекций и страниц переносится, но встроенные базы данных Notion (не Markdown-документы) при импорте не сохраняются как интерактивные таблицы.
Как включить полнотекстовый поиск?
Он включён по умолчанию и работает через встроенный поиск PostgreSQL (tsvector) — отдельного сервиса вроде Elasticsearch поднимать не нужно, поиск индексируется автоматически при сохранении документа.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →