Каждый растущий бизнес рано или поздно упирается в «налог на масштабирование» облачных CRM: тарифы AmoCRM, HubSpot и Битрикс24 привязаны к количеству рабочих мест, и при расширении отдела продаж с 5 до 25 менеджеров бюджет на подписку взлетает в геометрической прогрессии. К финансовым издержкам добавляются жесткие риски: блокировки аккаунтов, привязка данных к закрытым проприетарным базам и потенциальные претензии по закону 152-ФЗ «О персональных данных», если серверы провайдера находятся в непрозрачной инфраструктуре. Twenty CRM — это современная открытая CRM-система нового поколения с более чем 59 000 звёзд на GitHub, написанная на TypeScript и GraphQL. В отличие от громоздких систем прошлого века, Twenty предлагает молниеносный интерфейс в духе Notion и Linear, неограниченное число пользователей, гибкие кастомные объекты и полное владение базой данных на вашем собственном VDS. В этом практическом руководстве мы развернем свежий стабильный релиз Twenty CRM v2.51.0 в Docker Compose с PostgreSQL 16, настроим реверс-прокси Nginx с SSL и защитим инфраструктуру от сетевых угроз.
1. Почему бизнес уходит из облачных CRM: стоимость мест, санкции и 152-ФЗ
Облачная подписочная модель (SaaS) удобна для быстрого старта соло-предпринимателя, но по мере роста компании превращается в одну из самых обременительных статей расходов. Практика эксплуатации коммерческих CRM выявляет четыре ключевых уязвимости:
- Подушная тарификация (Per-Seat Pricing). Подписка на 10–20 менеджеров в популярных облачных сервисах обходится компании в 150 000–350 000 рублей в год. При этом функции распределения ролей, кастомных воронок и интеграций с вебхуками разработчики облаков часто блокируют на базовых тарифах, принудительно подталкивая бизнес к переходу на дорогие Enterprise-планы.
- Риски внезапных отключений и потери доступа. Блокировка аккаунта из-за сбоя биллинга, санкционных ограничений или формальной жалобы может парализовать отдел продаж за секунды. В закрытых SaaS-системах выгрузить накопленную годами историю сделок, записи звонков и переписки клиентов в чистом виде практически невозможно.
- Требования безопасности и 152-ФЗ. Контактные данные клиентов, реквизиты юридических лиц и финансовые условия договоров — это конфиденциальная информация. Хранение этой базы на сторонних облачных серверах всегда сопряжено с рисками скрытых утечек через недобросовестных подрядчиков. Развертывание CRM на собственном российском VDS с шифрованием дисков гарантирует, что доступ к базе имеет только владелец бизнеса.
- Ограничения кастомизации. Бизнес-процессы реальных компаний редко укладываются в стандартную жесткую модель «Сделка — Контакт — Компания». Когда требуется создать нестандартную сущность (например, «Партия товара», «Объект недвижимости», «Маршрут доставки»), большинство облачных CRM требуют написания сложных платных виджетов.
| Критерий | AmoCRM | Битрикс24 (Облако) | HubSpot | Twenty CRM (Self-Hosted) |
|---|---|---|---|---|
| Оплата за пользователей | За каждого юзера | Пакеты пользователей | Высокая (валюта) | 0 ₽ (без лимитов) |
| Хранение базы данных | Облако провайдера | Облако провайдера | Зарубежные ДЦ | Свой VDS в РФ (PostgreSQL) |
| Кастомные объекты | Ограничены | Сложные смарт-процессы | Только в Enterprise | Встроены из коробки |
| Доступ к сырым данным | Только через API | Ограничен | Ограничен | Полный SQL и pg_dump |
| Лицензия | Проприетарная | Проприетарная | Проприетарная | AGPLv3 (Открытый код) |
2. Архитектура Twenty: модули Server, Worker, PostgreSQL 16 и Redis
Репозиторий twentyhq/twenty развивается по модели открытого монорепозитория на современном технологическом стеке. В отличие от тяжелых систем на PHP или Java, Twenty построена на микросервисной связке NestJS и React с использованием GraphQL API:
- Twenty Server (контейнер
server): основное Node.js-приложение, обслуживающее входящие HTTP-запросы, отдающее статическую сборку веб-интерфейса и исполняющее GraphQL-запросы клиентов. Слушает внутренний порт3000. - Twenty Worker (контейнер
worker): фоновый обработчик асинхронных задач, запускаемый черезyarn worker:prod. Обрабатывает очереди синхронизации почты, вебхуки, запланированные задачи и тяжелые фоновые миграции без блокировки пользовательского интерфейса. - PostgreSQL 16 (контейнер
db): реляционная база данных, хранящая системную схему, метаданные кастомных объектов и все коммерческие записи. Twenty активно использует возможности JSONB и транзакционные механизмы Postgres 16. - Redis (контейнер
redis): высокоскоростное in-memory хранилище для очередей задач BullMQ и распределенного кэширования сессий. В манифесте обязательно настраивается с политикой--maxmemory-policy noeviction, предотвращающей случайную потерю фоновых очередей при нехватке памяти.
💡 Совет инженера: почему разделение Server и Worker обязательно
В продакшене нельзя объединять интерактивные HTTP-запросы менеджеров и тяжелую обработку импорта сотен тысяч контактов в один процесс. Архитектура Twenty выносит очереди воркера в отдельный контейнер, благодаря чему интерфейс CRM остается отзывчивым даже во время массированного обмена данными по API.
3. Системные требования к серверу: процессор, память и диски NVMe
В отличие от простых статических сайтов, Twenty CRM запускает полноценный Node.js бэкенд, асинхронный воркер, сервер базы данных PostgreSQL 16 и Redis. Для комфортной командной работы требуется правильно подобрать параметры виртуального сервера (VDS):
| Параметр сервера | Минимальный тариф (1–3 менеджера) | Рекомендуемый тариф (Production, 5–25 человек) |
|---|---|---|
| Процессор (vCPU) | 2 vCPU (от 2.6 ГГц) | 4 vCPU (от 3.3 ГГц) |
| Оперативная память (RAM) | 4 ГБ (с обязательным swap 2 ГБ) | 8 ГБ RAM |
| Дисковое пространство | 30 ГБ NVMe | 60–80 ГБ NVMe |
| Операционная система | Ubuntu 24.04 LTS / Debian 12 | Ubuntu 24.04 LTS x86_64 |
Особое внимание уделите типу дисковой подсистемы: реляционная база PostgreSQL при активном поиске по контактам и фильтрации сделок чувствительна к случайному чтению и записи (IOPS). Стандартные медленные SATA/SAS диски не подходят — выбирайте тарифы с быстрыми накопителями NVMe.
4. Развертывание Twenty CRM в Docker Compose: секреты и привязка портов
Развертывание Twenty строится с учетом строгих правил информационной безопасности SysKit:
- Привязка порта только к локальной петле (
127.0.0.1:3000:3000). Это защищает внутренний порт веб-сервиса от прямого сканирования из интернета и исключает обход правил межсетевого экрана UFW в Linux (эту уязвимость мы подробно анализировали в материале Docker обходит UFW на Linux). - Криптостойкие ключи шифрования. Twenty использует
ENCRYPTION_KEYдля защиты токенов интеграций и пользовательских секретов. Потеря этого ключа сделает невозможным расшифровку базы данных. - Изоляция прав доступа (
no-new-privileges). Защищает хост от попыток повышения привилегий при компрометации процессов контейнера.
Генерация ключей и подготовка структуры проекта
# Создаём рабочий каталог для стека CRM
mkdir -p /opt/twenty && cd /opt/twenty
# Генерируем пароль к базе данных (32 байта hex, без спецсимволов)
DB_PASS=$(openssl rand -hex 32)
# Генерируем мастер-ключ сквозного шифрования (32 байта base64)
ENC_KEY=$(openssl rand -base64 32)
# Генерируем секрет приложения
APP_SEC=$(openssl rand -base64 32)
# Создаём защищенный файл конфигурации .env
cat <<EOF > .env
TAG=v2.51.0
SERVER_URL=https://crm.example.ru
# Параметры базы данных PostgreSQL 16
PG_DATABASE_USER=twenty
PG_DATABASE_PASSWORD=${DB_PASS}
PG_DATABASE_HOST=db
PG_DATABASE_PORT=5432
PG_DATABASE_NAME=twenty
# Кэш и очереди Redis
REDIS_URL=redis://redis:6379
# Ключи безопасности и шифрования данных
ENCRYPTION_KEY=${ENC_KEY}
APP_SECRET=${APP_SEC}
# Тип хранилища файлов и аватаров (local или s3)
STORAGE_TYPE=local
EOF
# Защищаем файл с секретами от посторонних пользователей
chmod 600 .env
💡 Совет инженера: как сгенерировать стойкие пароли онлайн
Если вы настраиваете стек вручную, сгенерируйте криптостойкие пароли для баз данных через удобный генератор надёжных паролей SysKit. Выбирайте длину от 32 символов без знаков $ и /, которые могут конфликтовать со строкой подключения PostgreSQL.
Манифест docker-compose.yml
services:
server:
image: twentycrm/twenty:v2.51.0
container_name: twenty-server
restart: unless-stopped
env_file:
- .env
environment:
NODE_PORT: 3000
PG_DATABASE_URL: postgres://${PG_DATABASE_USER}:${PG_DATABASE_PASSWORD}@${PG_DATABASE_HOST}:${PG_DATABASE_PORT}/${PG_DATABASE_NAME}
SERVER_URL: ${SERVER_URL}
REDIS_URL: ${REDIS_URL}
STORAGE_TYPE: ${STORAGE_TYPE}
ENCRYPTION_KEY: ${ENCRYPTION_KEY}
APP_SECRET: ${APP_SECRET}
volumes:
- server_storage:/app/packages/twenty-server/.local-storage
ports:
# Защита от прямого сканирования: слушаем строго 127.0.0.1
- "127.0.0.1:3000:3000"
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
security_opt:
- no-new-privileges:true
mem_limit: 2.5g
cpus: 2.0
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
worker:
image: twentycrm/twenty:v2.51.0
container_name: twenty-worker
restart: unless-stopped
command: ["yarn", "worker:prod"]
env_file:
- .env
environment:
PG_DATABASE_URL: postgres://${PG_DATABASE_USER}:${PG_DATABASE_PASSWORD}@${PG_DATABASE_HOST}:${PG_DATABASE_PORT}/${PG_DATABASE_NAME}
SERVER_URL: ${SERVER_URL}
REDIS_URL: ${REDIS_URL}
STORAGE_TYPE: ${STORAGE_TYPE}
ENCRYPTION_KEY: ${ENCRYPTION_KEY}
APP_SECRET: ${APP_SECRET}
volumes:
- server_storage:/app/packages/twenty-server/.local-storage
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
security_opt:
- no-new-privileges:true
mem_limit: 2g
cpus: 1.5
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
db:
image: postgres:16-alpine
container_name: twenty-db
restart: unless-stopped
environment:
POSTGRES_USER: ${PG_DATABASE_USER}
POSTGRES_PASSWORD: ${PG_DATABASE_PASSWORD}
POSTGRES_DB: ${PG_DATABASE_NAME}
volumes:
- db_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${PG_DATABASE_USER} -d ${PG_DATABASE_NAME}"]
interval: 10s
timeout: 5s
retries: 5
security_opt:
- no-new-privileges:true
mem_limit: 2g
cpus: 1.5
redis:
image: redis:7-alpine
container_name: twenty-redis
restart: unless-stopped
command: ["redis-server", "--maxmemory-policy", "noeviction"]
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
security_opt:
- no-new-privileges:true
mem_limit: 512m
cpus: 1.0
volumes:
db_data:
redis_data:
server_storage:
Запуск контейнеров и первый старт
# Запускаем стек контейнеров в фоновом режиме
docker compose up -d
# Проверяем состояние всех четырех сервисов
docker compose ps
# Отслеживаем выполнение первоначальных миграций базы данных
docker compose logs --tail=100 -f server
При первом старте контейнер server автоматически применит миграции схемы базы данных PostgreSQL 16. Это занимает от 30 до 90 секунд в зависимости от скорости дисков VDS. Дождитесь появления в логах сообщения о готовности NestJS к приему входящих соединений на порту 3000.
5. Настройка Nginx с SSL Let's Encrypt и поддержкой WebSocket
Поскольку контейнер twenty-server привязан к локальной петле 127.0.0.1:3000, для защищенного доступа менеджеров через интернет настраивается обратный прокси Nginx. Twenty CRM активно использует протокол WebSocket для синхронизации канбан-досок и мгновенного отображения изменений сделок между несколькими сотрудниками без перезагрузки страницы.
Конфигурация виртуального хоста Nginx
Создайте конфигурационный файл для вашего домена (например, /etc/nginx/sites-available/crm.example.ru.conf):
# Ограничение размера загружаемых файлов для вложений и документов
client_max_body_size 50M;
server {
listen 80;
server_name crm.example.ru;
# Автоматический редирект HTTP -> HTTPS
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name crm.example.ru;
# Пути к сертификатам Let's Encrypt
ssl_certificate /etc/letsencrypt/live/crm.example.ru/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/crm.example.ru/privkey.pem;
# Рекомендуемые параметры безопасности SSL/TLS
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
# Заголовки безопасности
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
# Критично для Twenty: поддержка WebSocket соединений
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;
# Таймауты для длительных WebSocket сессий
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
}
}
Получение бесплатного SSL-сертификата Let's Encrypt
# Активируем конфигурационный файл виртуального хоста
ln -s /etc/nginx/sites-available/crm.example.ru.conf /etc/nginx/sites-enabled/
# Проверяем синтаксис Nginx перед перезагрузкой
nginx -t
# Выпускаем сертификат с помощью утилиты Certbot
certbot --nginx -d crm.example.ru --non-interactive --agree-tos --redirect -m admin@example.ru
# Перезагружаем Nginx для применения SSL
systemctl reload nginx
После настройки проверьте корректность цепочки сертификатов через сервис проверки SSL-сертификатов и убедитесь в отсутствии опасных уязвимостей через сканер анализа заголовков HTTP.
Первоначальный вход и мастер настройки Workspace
Откройте в браузере адрес https://crm.example.ru. При первом посещении система предложит создать учетную запись главного администратора (Owner):
- Укажите рабочий Email и задайте надежный пароль администратора.
- Задайте название компании (Workspace Name) и выберите часовой пояс вашего региона (например,
Europe/Moscow). - В разделе Settings → Workspace Members можно отправить приглашения менеджерам отдела продаж по электронной почте или сгенерировать прямые ссылки для входа.
Рекомендуемые VDS-провайдеры
Отказоустойчивые сервера с быстрыми NVMe и каналом до 1 Гбит/с
Timeweb Cloud — Быстрые NVMe VDS
Высокочастотные процессоры 3.3 ГГц и скоростные NVMe-накопители для молниеносной работы базы PostgreSQL 16 и очередей Twenty CRM.
Selectel — Инфраструктура Tier III
Отказоустойчивая серверная база в дата-центрах РФ уровня Tier III, приватные сети и гарантированное соблюдение требований 152-ФЗ.
Beget — Простой старт для домена
Быстрое развертывание Docker на Ubuntu 24.04, понятная панель управления и моментальный выпуск SSL-сертификатов.
6. Интеграция с Telegram и внешними сервисами: вебхуки и GraphQL API
Одно из главных преимуществ открытой CRM перед закрытыми облачными сервисами — отсутствие искусственных ограничений на вызовы API. В Twenty CRM все действия, выполняемые через веб-интерфейс, доступны через строго типизированный GraphQL API и REST-эндпоинты.
Автоматическое оповещение менеджеров в Telegram через вебхуки
В разделе Settings → Developers → Webhooks вы можете настроить отправку событий при создании новой сделки или лида на ваш локальный обработчик (например, в n8n или собственный скрипт-бот):
- Укажите целевой URL вебхука (например, входящий триггер сценария n8n).
- Выберите события:
opportunity.created(создана новая сделка) иperson.created(добавлен новый контакт). - Сценарий n8n или микросервис принимает JSON-пакет и мгновенно отправляет форматированную карточку клиента в рабочий Telegram-чат отдела продаж.
Пример добавления лида в Twenty CRM через Python
Для захвата заявок с лендингов или мобильного приложения используйте стандартный вызов GraphQL API:
import requests
CRM_URL = "https://crm.example.ru/graphql"
# API-ключ генерируется в панели: Settings -> Developers -> API Keys
API_KEY = "ваш_секретный_api_ключ"
query = """
mutation CreateNewPerson($input: PersonCreateInput!) {
createPerson(data: $input) {
id
name {
firstName
lastName
}
emails {
primaryEmail
}
phones {
primaryPhoneNumber
}
}
}
"""
variables = {
"input": {
"name": {
"firstName": "Алексей",
"lastName": "Смирнов"
},
"emails": {
"primaryEmail": "smirnov@example.ru"
},
"phones": {
"primaryPhoneNumber": "+79991234567"
}
}
}
headers = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json"
}
response = requests.post(CRM_URL, json={"query": query, "variables": variables}, headers=headers, timeout=10)
response.raise_for_status()
result = response.json()
print("Контакт успешно создан в Twenty CRM:", result["data"]["createPerson"])
7. Безопасность и оптимизация: тюнинг PostgreSQL 16 и защита Redis
При одновременной работе нескольких менеджеров и активной выгрузке отчетов производительность CRM зависит от двух узких мест: базы данных и брокера очередей.
Тюнинг параметров PostgreSQL под нагрузки CRM
Стандартная конфигурация PostgreSQL в Docker рассчитана на минимальное потребление ресурсов (128 МБ памяти). Для сервера с 4–8 ГБ оперативной памяти рекомендуется добавить в конфигурацию базы параметры оптимизации кэша:
# Рекомендуемые параметры для PostgreSQL 16 (сервер с 4–8 ГБ RAM)
shared_buffers = 1GB # 25% от доступной памяти VDS
effective_cache_size = 3GB # 50–75% от доступной памяти
maintenance_work_mem = 256MB
checkpoint_completion_target = 0.9
wal_buffers = 16MB
default_statistics_target = 100
random_page_cost = 1.1 # Оптимизация для скоростных NVMe накопителей
work_mem = 16MB
Защита очередей задач в Redis: директива noeviction
Фоновый воркер Twenty CRM использует библиотеку BullMQ для гарантированной доставки писем, очередей вебхуков и фоновых пересчетов аналитики. Если в настройках Redis включить стандартную политику вытеснения ключей по алгоритму LRU, при заполнении памяти Redis начнет удалять запланированные задачи, что приведет к потерям данных.
Именно поэтому в строке запуска контейнера redis обязательно задается флаг --maxmemory-policy noeviction: при достижении лимита памяти сервер вернет ошибку записи, но никогда не удалит существующую очередь задач.
8. Автоматический бэкап базы PostgreSQL и файлов в S3 с ротацией
Потеря клиентской базы данных или истории сделок — критический инцидент для любого бизнеса. Поскольку в архитектуре Twenty CRM все данные хранятся в реляционной базе данных PostgreSQL 16 и локальном хранилище файлов server_storage, процедура резервного копирования настраивается через регулярный дамп:
Скрипт автоматического создания дампа БД
#!/usr/bin/env bash
set -euo pipefail
BACKUP_DIR="/var/backups/twenty"
TIMESTAMP="$(date +%Y%m%d-%H%M%S)"
DUMP_FILE="${BACKUP_DIR}/twenty-db-${TIMESTAMP}.sql.gz"
mkdir -p "${BACKUP_DIR}"
# Создаём сжатый дамп базы PostgreSQL прямо из контейнера
docker exec twenty-db pg_dump -U twenty twenty | gzip -9 > "${DUMP_FILE}"
# Проверяем целостность полученного архива
gzip -t "${DUMP_FILE}"
echo "Бэкап успешно создан: ${DUMP_FILE} ($(du -h "${DUMP_FILE}" | cut -f1))"
# Удаляем локальные дампы старше 14 дней
find "${BACKUP_DIR}" -name 'twenty-db-*.sql.gz' -mtime +14 -delete
Сохраните скрипт по пути /usr/local/bin/backup-twenty.sh, сделайте исполняемым через chmod +x и добавьте в планировщик crontab -e для ночного выполнения в 03:00.
Для долгосрочного хранения архивов и защиты от выхода из строя физического накопителя настройте автоматическую выгрузку в отечественное облако S3 по нашему руководству по автоматическим шифрованным бэкапам VDS в S3.
Обновление Twenty CRM до новых версий
# Переходим в каталог проекта
cd /opt/twenty
# Обновляем версию образа в .env (например, меняем TAG=v2.52.0)
# Скачиваем свежие образы
docker compose pull
# Перезапускаем контейнеры без потери данных в томах
docker compose up -d
# Проверяем успешное применение новых миграций
docker compose logs --tail=50 -f server
9. Чек-лист проверки готовности Twenty CRM к коммерческой эксплуатации
| Элемент инфраструктуры | Требование безопасности / надежности | Назначение настройки |
|---|---|---|
| Сетевой порт 3000 | 127.0.0.1:3000:3000 |
Защита от обхода UFW и сканирования сервиса из интернета |
| Ключ шифрования | Стойкий ENCRYPTION_KEY (32 байта) |
Защита токенов интеграций и чувствительных данных в БД |
| Политика Redis | --maxmemory-policy noeviction |
Предотвращение случайного удаления фоновых очередей BullMQ |
| Лимиты контейнеров | Параметры mem_limit и cpus |
Защита операционной системы хоста от зависания по OOM |
| Протокол WebSocket | Заголовки Upgrade в Nginx |
Живая синхронизация канбан-досок и сделок без задержек |
| Резервное копирование | Ежедневный pg_dump + ротация 14 дней | Гарантия восстановления базы при аппаратных авариях |
Развертывание Twenty CRM на собственном VDS позволяет компании избавиться от ежемесячных счетов за каждого менеджера, сохраняет конфиденциальность коммерческой информации в пределах российской юрисдикции и дает полную свободу автоматизации через GraphQL API и вебхуки.
🎯 Подберите производительный VDS для корпоративной CRM
Для быстрой работы базы данных PostgreSQL 16 и очередей воркера критически важна высокая частота ядер процессора и быстрый накопитель NVMe. Выбирайте надежных российских хостинг-провайдеров с дата-центрами уровня Tier III для бесперебойной работы отдела продаж.