Привет, с вами снова Александр Константинов из Cloud.ru. Раньше я пользовался Notion, хранил там свои заметки, обучающие материалы, данные по своим стартапам. Но зарубежные SaaS-провайдеры ушли, и моя база превратилась в кирпич: она есть, но легально пользоваться ей невозможно. И это еще позитивный сценарий, потому что провайдер мог просто все безвозвратно удалить.
Сейчас, конечно, появляются другие сервисы, но все-таки у SaaS есть некоторые ограничения. И основное из них в том, что вектор их развития не подвластен пользователю. Плюс данные хранятся где-то там, кто-то ими управляет, но не я. А хочется все-таки делать это самостоятельно — это же моя база.
Я решил развернуть базу-знаний на wiki-движке Outline, потому что это полная замена Notion. У него хорошая функциональность, он простой в работе и с понятным интерфейсом. Что у меня получилось и как такое повторить, подробно рассказал в статье.

Поскольку я работаю в облачном провайдере, то было логично попробовать развернуть базу знаний на виртуальной машине и посчитать, сколько это будет стоить. Забегая вперед, скажу, что база знаний объемом в 10 000 заметок в облаке обошлась мне в 146 рублей в месяц. И так не потому, что я сотрудник компании, а потому что для этой цели можно использовать виртуальную машину и объектное хранилище с free tier. Если подключить к базе AI или масштабировать ее до уровня корпоративного решения, тогда да, придется запускать еще другие сервисы, а они уже дороже.
Что будем делать и что нам понадобится
Cоздадим виртуальную машину Ubuntu 22.04, настроим для нее публичный IP-адрес, создадим бакет в Object Storage и настроим CORS для него.
На виртуальной машине настроим Docker и Docker Compose, развернем сервис Outline, подключим его к Object Storage и GitLab и опубликуем на сервере nginx, выпустим SSL-сертификат в сервисе Let’s Encrypt.
В итоге получим надежную схему, где файлы хранятся в Object Storage, а клиентский трафик шифруется HTTPS.
Какие будем использовать сервисы:
Виртуальная машина с free tier — бесплатная ВМ заданной конфигурации. Настройки поменять не получится, но ее ресурсов хватит для базы знаний, в которой поместится до 10 000 заметок и которой будут пользоваться до 20 человек. Если нужно больше, придется выбрать другую виртуальную машину, но уже без free tier.
Публичный IP-адрес для доступа к базе знаний через интернет — 146 рублей в месяц.
Объектное хранилище с free tier — бесплатные 15 ГБ в стандартном S3-хранилище каждый месяц. Если потребуется больше места, то хранилище будет платное.
Docker — система контейнеризации.
Docker Compose — инструмент для запуска и управления Docker-контейнерами.
Outline — open-source система вики.
Бесплатный сервис nip.io для получения публичного доменного имени и сертификата. Я использую собственное зарегистрированное доменное имя и SSL-сертификат для организации доступа.
Nginx — веб-сервер для проксирования запросов и организации защищeнного HTTPS-доступа к базе знаний.
Let’s Encrypt — сервис для автоматического получения бесплатного SSL-сертификата.
GitLab — как провайдер для авторизации. Я выбрал его, потому что там хранятся мои проекты и мне удобно работать с ним. Список других доступных провайдеров можно найти в документе по аутентификации Outline.
Evolution Foundation Models — популярные open source модели в облаке, готовые к использованию. Сервис бесплатный до конца октября. Дальше будет тарификация по токенам в среднем 35 рублей за чтение и 70 рублей за запись. Цена за миллион токенов.
Для большой корпоративной базы с сотнями пользователей будут нужны дополнительные сервисы:
Keycloak для единой точки входа (SSO),
Kubernetes для деплоя сервисов и масштабирования,
Managed PostgreSQL и Managed Redis.
О разных вариантах развертывания корпоративных баз знаний с архитектурными схемами в облаке рассказываем на сайте, посмотрите, если интересно.
Шаги:
Перед началом работы
Регистрируемся в личном кабинете Cloud.ru или входим под своей учетной записью.
Генерируем SSH-ключ по инструкции.
Загружаем публичную часть SSH-ключа в облако Cloud.ru Evolution по инструкции.
1. Разворачиваем ресурсы в облаке
Создадим группу безопасности, виртуальную машину и бакет в Object Storage.
1. Создаем новую группу безопасности со следующими параметрами:
Указываем Название группы безопасности, например, outline-wiki.
Добавляем правила входящего и исходящего трафика.
Правила входящего трафика:
a. Протокол: TCP
b. Порт: 443
c. Тип источника: IP-адрес
d. Источник: 0.0.0.0/0
a. Протокол: TCP
b. Порт: 80
c. Тип источника: IP-адрес
d. Источник: 0.0.0.0/0
Правила исходящего трафика:
a. Протокол: Любой
b. Тип адресата: IP-адрес
c. Адресат: 0.0.0.0/0
2. Проверяем, что в личном кабинете на странице сервиса «Группы безопасности»:
отображается группа безопасности outline-wiki;
статус группы безопасности — «Создана».
3. Создаем бесплатную виртуальную машину со следующими параметрами:
В поле Название указываем название виртуальной машины, например, outline-wiki.
На вкладке Публичные выбираем образ Ubuntu 22.04.
В поле Логин указываем логин пользователя виртуальной машины, например, outline.
В разделе Метод аутентификации выбираем публичный ключ и пароль.
Указываем публичный ключ и пароль для создаваемого пользователя.
В поле Имя хоста указываем уникальное имя устройства, по которому можно идентифицировать виртуальную машину в сети, например, outline-wiki.
В поле Название загрузочного диска указываем outline-wiki-disk.
Включаем опцию Подключить публичный IP.
В группе Тип IP-адреса выбираем Прямой.
Выбираем группы безопасности SSH-access_ru.AZ-1, outline-wiki.
4. Проверяем, что в личном кабинете на странице сервиса «Виртуальные машины»:
отображается виртуальная машина outline-wiki;
статус виртуальной машины — «Запущена».
5. Создаем бакет в Object Storage со следующими параметрами:
В поле Доменное имя указываем outline-wiki (должно быть уникальным, замените на свое уникальное значение).
В поле Название указываем outline-wiki (совпадает с доменным именем).
В поле Глобальное название указываем outline-wiki (совпадает с доменным именем).
В поле Класс хранения по умолчанию выбираем стандартный.
В поле Максимальный размер указываем 10 ГБ.
6. Переходим в раздел Object Storage API. Сохраняем значения ID тенанта и Регион.
7. Проверяем, что в личном кабинете на странице сервиса Object Storage отображается бакет outline-wiki.
8. Создаем сервисный аккаунт администратора со следующими параметрами:
В поле Название указываем outline-object-storage-admin.
В поле Описание указываем «Аккаунт администратора Object Storage».
В поле Проект выбираем Пользователь сервисов.
Оставляем список Сервисы пустым.
В разделе Evolution Object Storage Роли выбираем s3e.admin.
9. Следуя аналогичной инструкции, создаем сервисный аккаунт пользователя со следующими параметрами:
В поле Название указываем outline-object-storage.
В поле Описание указываем «Аккаунт пользователя Object Storage».
В поле Проект выбираем Пользователь сервисов.
Оставляем список Сервисы пустым.
В поле Evolution Object Storage Роли выбираем s3e.viewer, s3e.editor.
10. Генерируем ключи доступа для обоих аккаунтов. Сохраняем Secret ID и Secret Key для обоих ключей.
2. Настраиваем окружение на виртуальной машине
Настраиваем систему и устанавливаем необходимые пакеты на виртуальной машине.
1. Подключаемся к виртуальной машине outline-wiki через серийную консоль или по SSH.
2. Обновляем систему и устанавливаем необходимые зависимости:
sudo apt update && sudo apt upgrade -y
sudo apt install unzip gnupg software-properties-common apt-transport-https ca-certificates python3-pip nginx snapd -y
sudo snap install core; sudo snap refresh core
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
3. Устанавливаем Docker и Docker Compose:
# Add Docker's GPG key
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
# Add Docker repository
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# Install Docker
sudo apt update && sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin docker-compose
# Add user to docker group
sudo usermod -aG docker $USER
newgrp docker
4. Проверяем, что Docker установлен корректно:
docker --version
docker compose version
3. Настраиваем nginx и HTTPS
Настраиваем службу nginx и обеспечиваем доступ по HTTPS.
1. Подключаемся к виртуальной машине outline-wiki через серийную консоль или по SSH.
2. Конфигурируем файрвол:
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable
3. Создаем конфигурационный файл:
sudo nano /etc/nginx/sites-available/outline.conf
4. Вставляем конфигурацию, заменив <IP-адрес> на IP-адрес нашей виртуальной машины:
server {
listen 80;
server_name wiki.<IP-адрес>.nip.io www.wiki.<IP-адрес>.nip.io;
location / {
proxy_pass http://localhost:3000/;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_redirect off;
}
}
5. Применяем конфигурацию и перезапускаем nginx:
sudo ln -sf /etc/nginx/sites-available/outline.conf /etc/nginx/sites-enabled/outline.conf
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t
sudo systemctl reload nginx
6. Проверяем, что nginx работает:
sudo systemctl status nginx
Cервис nginx должен быть в статусе «active (running)».
7. Переходим по адресу http://wiki.<IP-адрес>.nip.io. Откроется страница с текстом 502 Bad Gateway.
8. Запускаем команду для выпуска SSL-сертификата:
sudo certbot --nginx -d wiki.<IP-адрес>.nip.io --redirect --agree-tos -m <EMAIL>
Где:
<IP‑адрес> — IP‑адрес вашей виртуальной машины.
<EMAIL> — ваш email.
9. После успешного выпуска сертификата, переходим по адресу https://wiki.<IP-адрес>.nip.io. Откроется страница с текстом 502 Bad Gateway. В свойствах сайта браузер отметит соединение как безопасное.
4. Настраиваем приложение в GitLab
Создаем приложение в GitLab-инстансе для интеграции с Outline.
1. Переходим в Настройки → Приложения в собственном или облачном GitLab-инстансе.
2. Создаем новое приложение со следующими настройками:
Имя: Outline
Redirect URI: https://wiki.<IP-адрес>.nip.io/auth/oidc.callback (меняем значение IP-адрес)
Scopes: выбираем openid, profile и email
3. Сохраняем приложение.
4. Сохраняем значения Application ID и Secret, они понадобятся в дальнейшем.
5. Разворачиваем приложение
Разворачиваем серверное приложение Outline с помощью Docker Compose.
1. Подключаемся к виртуальной машине outline-wiki через серийную консоль или по SSH.
2. Создаем структуру проекта:
mkdir -p $HOME/outline
cd $HOME/outline
3. Генерируем уникальные ключи и сохраняем их, они понадобятся в дальнейшем:
# Generate two random secrets for Outline
openssl rand -hex 32 # Save this as SECRET_KEY
openssl rand -hex 32 # Save this as UTILS_SECRET
# Generate database password
openssl rand -base64 15 # Save this as POSTGRES_PASSWORD
4. Создаем файл docker-compose.yml:
nano docker-compose.yml
5. Вставляем содержимое в файл docker-compose.yml, заменив переменные на значения:
Код
services:
outline:
image: flameshikari/outline-ru:0.86.0
env_file: ./docker.env
ports:
- "3000:3000"
volumes:
- storage-data:/var/lib/outline/data
depends_on:
- postgres
- redis
environment:
PGSSLMODE: disable
redis:
image: redis:7-alpine
ports:
- "6379:6379"
command: ["redis-server", "--bind", "0.0.0.0", "--port", "6379"]
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 30s
retries: 3
postgres:
image: postgres:15
env_file: ./docker.env
ports:
- "5432:5432"
volumes:
- database-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD", "pg_isready", "-d", "outline", "-U", "user"]
interval: 30s
timeout: 20s
retries: 3
environment:
POSTGRES_USER: 'user'
POSTGRES_PASSWORD: <POSTGRES_PASSWORD>
POSTGRES_DB: 'outline'
volumes:
storage-data:
database-data:
Где:
<POSTGRES_PASSWORD> — пароль от базы данных, сгенерированный ранее.
6. Создаем конфигурацию Redis:
nano redis.conf
7. Вставляем содержимое в файл:
bind 127.0.0.1
port 6379
timeout 0
save 900 1
save 300 10
save 60 10000
dbfilename dump.rdb
dir ./
8. Создаем файл docker.env:
nano docker.env
9. Вставляем содержимое в файл, заменив переменные на значения:
Код
NODE_ENV=production
# Application URL
URL=https://wiki.<IP-адрес>.nip.io
PORT=3000
# Secrets (use the generated values from Step 6)
SECRET_KEY=<SECRET_KEY>
UTILS_SECRET=<UTILS_SECRET>
# Database configuration
DATABASE_URL=postgres://user:<POSTGRES_PASSWORD>@postgres:5432/outline
PGSSLMODE=disable
# Redis configuration
REDIS_URL=redis://redis:6379
# File storage (using AWS S3)
FILE_STORAGE=s3
AWS_ENDPOINT_URL_S3=https://s3.cloud.ru
AWS_SDK_LOAD_CONFIG=1
AWS_USE_GLOBAL_ENDPOINT=false
AWS_S3_ADDRESSING_STYLE=path
AWS_ACCESS_KEY_ID=<TENANT_ID>:<SECRET_KEY_ID>
AWS_SECRET_ACCESS_KEY=<SECRET_KEY>
AWS_REGION=<REGION>
AWS_S3_CUSTOM_DOMAIN=<BUCKET_NAME>.s3.cloud.ru
AWS_S3_ENDPOINT=https://<BUCKET_NAME>.s3.cloud.ru
AWS_S3_UPLOAD_BUCKET_URL=https://<BUCKET_NAME>.s3.cloud.ru
AWS_S3_UPLOAD_BUCKET_NAME=<BUCKET_NAME>
AWS_S3_FORCE_PATH_STYLE=false
AWS_S3_ACL=private
FILE_STORAGE_UPLOAD_MAX_SIZE=26214400
AWS_S3_SIGNATURE_VERSION=v4
# GitLab OIDC Authentication
OIDC_CLIENT_ID=<GITLAB_APP_ID>
OIDC_CLIENT_SECRET=<GITLAB_CLIENT_SECRET>
OIDC_AUTH_URI=https://<GITLAB_DOMAIN>/oauth/authorize
OIDC_TOKEN_URI=https://<GITLAB_DOMAIN>/oauth/token
OIDC_USERINFO_URI=https://<GITLAB_DOMAIN>/oauth/userinfo
OIDC_USERNAME_CLAIM=username
OIDC_DISPLAY_NAME=GitLab
OIDC_SCOPES=openid email profile
# SSL Configuration
FORCE_HTTPS=true
# Rate limiting
RATE_LIMITER_ENABLED=true
RATE_LIMITER_REQUESTS=1000
RATE_LIMITER_DURATION_WINDOW=60
# Updates
ENABLE_UPDATES=true
# Logging
DEBUG=http
LOG_LEVEL=info
Где:
<SECRET_KEY>, <UTILS_SECRET> — секреты, сгенерированные на шаге 5.
<POSTGRES_PASSWORD> — пароль от базы данных, сгенерированный ранее.
<TENANT_ID> — ID тенанта сервиса Object Storage.
<REGION> — регион Object Storage.
<SECRET_KEY_ID>, <SECRET_KEY> — ID ключа и секретный ключ доступа к Object Storage. Используем ключи от аккаунта outline-object-storage.
<BUCKET_NAME> — название бакета Object Storage.
<GITLAB_APP_ID>, <GITLAB_CLIENT_SECRET> — ID и секретный ключ доступа к приложению GitLab.
<GITLAB_DOMAIN> — адрес сервиса GitLab. Может быть собственный или https://gitlab.com/.
10. Запускаем сервис:
docker compose up -d
11. Проверяем, что сервисы запущены:
docker compose ps
12. Переходим по адресу https://wiki.<IP-адрес>.nip.io. Откроется страница Outline, нас перенаправят в GitLab для авторизации.
13. Авторизуемся в GitLab, и нас автоматически перенаправят на страницу Outline.

6. Настраиваем CORS в Object Storage
Настраиваем CORS для бакета в Object Storage, чтобы разрешить безопасное взаимодействие с вашим приложением.
1. Подключаемся к виртуальной машине outline-wiki через серийную консоль или по SSH .
2. Устанавливаем зависимости командой:
pip install boto3
3. Создаем файл configure_cors.py и добавляем в него код:
nano configure_cors.py
4. Вставляем содержимое в файл конфигурации:
Код
import sys
import boto3
from botocore.client import Config
BUCKET = sys.argv[1]
ENDPOINT = sys.argv[2]
AK = sys.argv[3]
SK = sys.argv[4]
REGION = sys.argv[5]
FRONTEND_URL = sys.argv[6]
s3 = boto3.client(
service_name='s3',
aws_access_key_id=AK,
aws_secret_access_key=SK,
endpoint_url=ENDPOINT,
region_name=REGION,
verify=False,
config=Config(s3={'addressing_style': 'virtual'})
)
cors_configuration = {
'CORSRules': [{
'AllowedMethods': ['PUT', 'POST'],
'AllowedOrigins': [FRONTEND_URL],
'ExposeHeaders': ['ETag'],
'AllowedHeaders': ['*'],
'MaxAgeSeconds': 60
}]
}
s3.put_bucket_cors(Bucket=BUCKET, CORSConfiguration=cors_configuration)
5. Запускаем команду для обновления CORS-правил:
python3 configure_cors.py <BUCKET_NAME> https://s3.cloud.ru <TENANT_ID>:<SECRET_KEY_ID> <SECRET_KEY> <REGION> https://wiki.<IP-адрес>.nip.io
Где:
<BUCKET_NAME> — название бакета Object Storage.
<TENANT_ID> — ID тенанта сервиса Object Storage.
<REGION> — регион Object Storage.
<SECRET_KEY_ID>, <SECRET_KEY> — ID ключа и секретный ключ доступа к Object Storage. Используем ключи от аккаунта outline-object-storage-admin.
6. Переходим по адресу http://<IP-адрес>.nip.io. Откроется страница Outline.
7. Создаем новую заметку и загружаем в нее изображение.
7. Удаляем доступ по SHH для виртуальной машины
Обеспечим безопасность, удалив доступ по SSH для нашей виртуальной машины, поскольку он больше не требуется.
1. Переходим в раздел Сетевые параметры.
2. Нажимаем изменить группы безопасности для публичного IP-адреса.
3. Удаляем группу SSH-access_ru.
4. Нажимаем Сохранить.
5. Проверяем, что доступа нет — пробуем подключиться к виртуальной машине по SSH.
8. Прикручиваем AI к базе знаний
У меня есть open source проект на GitVerse, он готовый: берешь и запускаешь. Получится Telegram-бот, который будет работать с популярными готовыми open source моделями в облаке и базой знаний.
Городить бота стоит, если:
в Outline накопились регламенты, памятки, HOW-TO;
нужен поиск по заголовкам или контенту в заметках;
хочется спрашивать «человеческими» словами и сразу получать выдержку и ссылку на заметку.
Ключевая идея такого бота в том, что это «ленивый RAG без своей базы». То есть пользователь пишет вопрос, бот делает запрос к Search API Outline, берет топ-N результатов, склеивает их в промпт и отдает в LLM-endpoint. Полученный ответ бот возвращает в чат или REST без сохранения состояния.
Если говорить подробнее про сам процесс поиска и ответа, то выглядит он так:
извлечение ключевых слов для поиска. Вопрос прогоняется через промпт в LLM и достаются ключевые слова для поиска через API;
ключевые слова передаются в Outline или search?query= — получаются релевантные страницы;
подготовка контекста: найденные фрагменты форматируются в заголовок, summary, ссылку;
AI-обработка: контекст отправляется в сервис Evolution Foundation Models для генерации развернутого ответа;
формирование ответа: бот возвращает результат на языке пользователя.
Меня всегда волнует вопрос безопасности, у бота для базы знаний с этим все в порядке:
проверка токенов: любой запрос к Outline выполняется только после проверки валидности JWT/Api-Token;
токены ограничивают область видимости данных (RBAC из коробки). Пользователь может прочитать только те данные, к которым он имеет доступ в сервисе;
изоляция сессий: каждый чат = отдельный контекст, данные не смешиваются.
При всем этом инфраструктуры для такого бота нужно минимум: один контейнер FastAPI + адаптер Telegram. На развертывание уйдет 5 минут на той же виртуальной машине с free tier, где лежит сама база знаний, поэтому докупать железа не придется.
В итоге получится легкий, но полезный ассистент: ни собственной БД, ни cron-индексаций, только API Outline и два вызова LLM.
Вместо заключения
Работать с SaaS, конечно, удобно: не нужно ничего деплоить, можно залогиниться и сразу начать работать. Но надо понимать, что в таком случае ваши данные — это как бы не ваши данные, ими фактически управляет кто-то другой.
База знаний в облаке лучше SaaS-решения тем, что ей можно полностью владеть и управлять: в любой момент сделать локальный бэкап, не зависеть от SaaS, самостоятельно доработать решение под свои требования. Например, интегрировать AI-ассистента в интерфейс самой базы.
Кстати, 23 октября я проведу вебинар «Создаем корпоративную базу знаний Outline c SSO и AI в облаке». Пишите вопросы в комментариях и подключайтесь, я на все подробно отвечу.
Комментарии (2)
ksokol
02.10.2025 15:12потому что это полная замена Notion
Я сам пользуюсь с удовольствием Outline, очень его ценю, но называть его полной заменой Notion это как-то совсем уж.
Notion это существенно больше, чем просто заметки.
rasperepodvipodvert
Аа, вы просто прорекламировали cloud.ru - понятно...