Что подготовить до установки: сервер, ОС и требования
Меня зовут Евгений Семёнов, я технический директор ITfresh. Мы обслуживаем компании до 50 рабочих мест в Москве, и EspoCRM я разворачиваю клиентам с 2020 года — на наших серверах в дата-центре МТС и на арендованных VPS. В этой статье — ровно тот процесс, по которому работает моя команда: три способа установки, реальные команды под Ubuntu 24.04 и список граблей, на которые мы наступали за вас. Если вы ещё выбираете CRM и не уверены, что вам нужна именно Espo, — начните с обзорной статьи серии, ссылка внизу. Здесь же — чистая практика.
Размер VPS: считаем от числа пользователей
Первый вопрос при заказе сервера — сколько брать ресурсов. Мой рабочий калькулятор, выверенный на десятке инсталляций:
| Рабочих мест | vCPU | RAM | Диск | Комментарий |
|---|---|---|---|---|
| До 15 | 2 | 2 ГБ | SSD 20 ГБ | Хватает с запасом, но без Docker-стека |
| 15–30 | 2 | 4 ГБ | SSD 40 ГБ | Наш самый ходовой тариф |
| 30–50 | 4 | 8 ГБ | SSD 60–80 ГБ | БД желательно с выделенным буфером |
Диск считайте с запасом под вложения: менеджеры прикладывают к сделкам сканы договоров и коммерческие предложения, и за год активной работы отдел из 20 человек спокойно наращивает 10–15 ГБ файлов. Дешевле сразу взять SSD побольше, чем через полгода переезжать.
ОС и стек: почему Ubuntu 24.04 LTS
Мы ставим на Ubuntu 24.04 LTS по прозаичной причине: это долгоживущий релиз с поддержкой до 2029 года, и в его штатных репозиториях лежит PHP 8.3 — ровно то, что нужно. Никаких сторонних PPA, никакой сборки из исходников: apt install — и стек готов. Официально EspoCRM рекомендует именно VPS или выделенный сервер, а не shared-хостинг, и я подписываюсь: на шареде вы не управляете ни версией PHP, ни cron, ни лимитами.
Требования EspoCRM 9.3
Актуальная ветка на момент публикации — 9.3 (вышла в феврале 2026, свежий патч 9.3.10 от 2 июля 2026). Требования такие:
- PHP 8.3–8.5. Обратите внимание: PHP 8.2 ветка 9.3 уже не поддерживает — в половине инструкций в сети до сих пор фигурирует 8.2, и это устаревшая информация.
- Расширения PHP (обязательные): pdo_mysql (или pdo_pgsql), gd с FreeType, openssl, zip, mbstring, iconv, curl, xml, xmlwriter, exif, bcmath. Опционально: zmq или ev для WebSocket, pcntl, posix, ldap.
- БД: MySQL 8.0+, MariaDB 10.3+ или PostgreSQL 15. Мы берём MariaDB — она в репозитории Ubuntu и не требует дополнительных телодвижений.
php.ini: значения, с которыми мы сдаём проекты
Официальные рекомендации совпадают с нашими боевыми значениями, поэтому привожу их как есть — правится в /etc/php/8.3/fpm/php.ini:
max_execution_time = 180
max_input_time = 180
memory_limit = 256M
post_max_size = 50M
upload_max_filesize = 50M
max_execution_time 180 критичен для будущих обновлений: апгрейд-скрипт работает дольше стандартных 30 секунд, и на дефолте вы получите оборванное обновление. post_max_size и upload_max_filesize ставьте под ваши реальные вложения: если клиент шлёт менеджерам чертежи по 80 МБ — поднимайте оба значения, они работают в паре.
Способ 1 — классическая установка на LEMP вручную
Этот способ мы выбираем, когда Espo будет жить на сервере не одна — рядом с сайтом, файлообменником или другой панелью — и нужен полный контроль над каждым конфигом. Он же — единственный путь по-настоящему понять, как система устроена.
Ставим nginx, PHP-FPM 8.3 и MariaDB
На свежей Ubuntu 24.04 весь стек прилетает из штатных репозиториев одной командой:
apt update && apt install -y nginx mariadb-server \
php8.3-fpm php8.3-mysql php8.3-gd php8.3-zip php8.3-mbstring \
php8.3-curl php8.3-xml php8.3-bcmath php8.3-imap php8.3-ldap unzip
После установки правим php.ini значениями из предыдущего раздела и перезапускаем pool: systemctl restart php8.3-fpm. В самом pool-конфиге (/etc/php/8.3/fpm/pool.d/www.conf) для отдела до 30 человек дефолтный pm = dynamic с pm.max_children = 5 маловат — мы ставим pm.max_children = 20, иначе в понедельник утром, когда все 25 менеджеров разом открывают CRM, часть запросов встаёт в очередь.
Наш эталонный server-блок nginx
Ключевая особенность современных версий Espo: корнем сайта должен быть каталог public/, а не корень дистрибутива. Плюс обязательно закрываем снаружи служебные каталоги — в data/ лежат конфиг с паролем БД и логи. Вот блок, который мы кладём в /etc/nginx/sites-available/crm:
server {
listen 80;
server_name crm.example.ru;
root /var/www/espocrm/public;
index index.php;
client_max_body_size 50M;
location /client {
alias /var/www/espocrm/client;
access_log off;
expires 30d;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_read_timeout 180;
}
location /api/v1/ {
try_files $uri /api/v1/index.php?$query_string;
}
location ~ /(data|application|custom|vendor)/ { deny all; }
location ~ /\. { deny all; }
}
Обратите внимание на fastcgi_read_timeout 180 — он должен совпадать с max_execution_time, иначе nginx оборвёт долгий запрос раньше, чем PHP его доработает. И на client_max_body_size 50M — без него лимиты php.ini бесполезны: nginx завернёт большой файл ещё на подлёте с ошибкой 413.
База данных: utf8mb4 и отдельный пользователь
mysql -u root
CREATE DATABASE espocrm CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'espocrm'@'localhost' IDENTIFIED BY 'СложныйПароль_сюда';
GRANT ALL PRIVILEGES ON espocrm.* TO 'espocrm'@'localhost';
FLUSH PRIVILEGES;
Кодировка именно utf8mb4 — старая utf8 в MySQL трёхбайтовая, и первый же эмодзи в комментарии менеджера уронит запись в базу. Root-доступ приложению не даём никогда: отдельный пользователь с правами только на свою схему.
Файлы, права и веб-инсталлер
cd /var/www
wget https://www.espocrm.com/downloads/EspoCRM-9.3.10.zip
unzip EspoCRM-9.3.10.zip && mv EspoCRM-9.3.10 espocrm
chown -R www-data:www-data /var/www/espocrm
find /var/www/espocrm -type d -exec chmod 755 {} \;
find /var/www/espocrm -type f -exec chmod 644 {} \;
Владелец — строго www-data: система на лету пишет в data/ кэш и конфиги, а в custom/ — ваши сущности из Entity Manager. Дальше открываем http://crm.example.ru в браузере и проходим веб-инсталлер: он сам прогоняет проверки (версия PHP, расширения, права на каталоги, доступ к БД) и подсвечивает красным всё, что не так. Вводим реквизиты базы из шага выше, создаём администратора — и через пару минут видим рабочий стол CRM. Если инсталлер ругается — не продавливайте «дальше», почините причину: девять из десяти проблем на этом шаге — забытое расширение PHP или права на каталоги.
Способ 2 — официальный espocrm-installer: сервер под ключ одной командой
Если под CRM выделен отдельный чистый VPS и нужен быстрый результат — есть официальный скрипт espocrm-installer (открытый, под лицензией Apache 2.0). Он сам разворачивает Docker-стек: контейнеры EspoCRM, NGINX и MariaDB, и по желанию сразу вешает сертификат Let's Encrypt. Запуск:
wget https://github.com/espocrm/espocrm-installer/releases/latest/download/install.sh
sudo bash install.sh -y --ssl --letsencrypt --domain=crm.example.ru
Флаг -y отвечает согласием на вопросы, --ssl --letsencrypt --domain — сразу поднимает HTTPS на вашем домене. Обязательное условие: A-запись домена уже должна указывать на IP сервера, иначе выпуск сертификата провалится. Через несколько минут скрипт печатает адрес и реквизиты администратора — можно заходить и работать.
Честно об ограничениях. Во-первых, у вас меньше контроля: конфиги nginx и PHP живут внутри структуры, которую создал скрипт, и любая нестандартная правка требует разбираться, как он всё разложил. Во-вторых, бэкап устроен иначе, чем при классической установке: копировать надо тома Docker и дамп БД из контейнера, а не привычный /var/www. В-третьих, соседство с другими сервисами на том же сервере — уже не его сценарий: скрипт считает, что порты 80/443 принадлежат ему.
После установки посмотрите, что скрипт создал на диске: структура каталогов с данными, конфигами и скриптами обслуживания лежит в домашнем каталоге установки. Там же — команды управления стеком: остановка, запуск, обновление. Прежде чем сдавать такой сервер в эксплуатацию, я всегда прогоняю тестовое восстановление из бэкапа — об этом ниже в чек-листе приёмки.
Способ 3 — docker compose своими руками
Третий путь — официальный образ espocrm/espocrm и свой compose-файл. Это наш выбор для клиентов, у которых на сервере уже живёт Docker-хозяйство: всё версионируется, переносится и обновляется предсказуемо. Образ существует в вариантах apache, fpm и fpm-alpine (плюс версионные теги вида 9.3.10-apache).
Правильный стек по рекомендации вендора — не один контейнер, а три плюс база: приложение, отдельный контейнер под фоновые задачи (daemon) и отдельный под WebSocket. Вот скелет нашего рабочего compose-файла:
services:
mariadb:
image: mariadb:11
environment:
MARIADB_ROOT_PASSWORD: root_pass
MARIADB_DATABASE: espocrm
MARIADB_USER: espocrm
MARIADB_PASSWORD: db_pass
volumes:
- mariadb-data:/var/lib/mysql
espocrm:
image: espocrm/espocrm:9.3.10-apache
environment:
ESPOCRM_DATABASE_HOST: mariadb
ESPOCRM_DATABASE_NAME: espocrm
ESPOCRM_DATABASE_USER: espocrm
ESPOCRM_DATABASE_PASSWORD: db_pass
ESPOCRM_ADMIN_USERNAME: admin
ESPOCRM_ADMIN_PASSWORD: admin_pass
ESPOCRM_SITE_URL: "https://crm.example.ru"
volumes:
- espocrm-data:/var/www/html/data
- espocrm-custom:/var/www/html/custom
- espocrm-client-custom:/var/www/html/client/custom
ports:
- "127.0.0.1:8080:80"
espocrm-daemon:
image: espocrm/espocrm:9.3.10-apache
entrypoint: docker-daemon.sh
volumes_from: [espocrm]
espocrm-websocket:
image: espocrm/espocrm:9.3.10-apache
entrypoint: docker-websocket.sh
volumes_from: [espocrm]
Три вещи, на которых спотыкаются. Первая — ESPOCRM_SITE_URL критичен: если он не совпадает с реальным адресом, по которому пользователи открывают CRM, посыплются ссылки в письмах и часть интерфейса. Вторая — тома: data, custom и client/custom обязаны быть внешними томами, иначе при пересоздании контейнера вы потеряете конфиг, вложения и все свои кастомные сущности. Третья — runtime-настройки удобно задавать переменными вида ESPOCRM_CONFIG_*: например, ESPOCRM_CONFIG_LOGGER__LEVEL: debug на время отладки, без правки файлов в контейнере.
Обновление сводится к смене тега образа на свежий патч и docker compose up -d — данные в томах, миграции контейнер выполняет сам. Порт публикуем только на localhost, а наружу смотрит nginx хоста с TLS — реверс-проксирование и грабля с WebSocket разобраны в следующем разделе.
Обязательная пост-установка, которую пропускают 9 из 10 инструкций
Инсталлер отработал, вы видите рабочий стол — и вот тут большинство инструкций заканчивается. А CRM в этот момент ещё наполовину мертва: не ходит почта, не срабатывают напоминания, нет живых уведомлений и HTTPS. Разбираю четыре обязательных шага.
Cron: без него не работает вообще ничего фонового
Вся фоновая жизнь Espo — забор почты по IMAP, отправка писем, напоминания, плановые задачи — крутится через cron. При классической установке добавляем задание пользователю www-data:
crontab -e -u www-data
* * * * * cd /var/www/espocrm; php -f cron.php > /dev/null 2>&1
Именно раз в минуту и именно под www-data — если запустить под root, тот наплодит в data/cache файлов с чужим владельцем, и веб-процесс потом не сможет их перезаписать (это грабля №1 из финального раздела). В Docker-стеке эту роль выполняет контейнер espocrm-daemon — отдельный cron не нужен, но проверьте, что контейнер запущен и не рестартует по кругу.
WebSocket: живые уведомления
Без WebSocket уведомления в интерфейсе появляются только после обновления страницы. С ним — мгновенно: менеджеру прилетело письмо или назначили задачу, и он видит это сразу. Для работы нужно PHP-расширение zmq или ev, включённый параметр useWebSocket в конфиге и запущенный процесс websocket.php (мы оформляем его systemd-юнитом; в Docker это готовый контейнер espocrm-websocket).
Грабля, на которую мы наступили в первый же раз за реверс-прокси: nginx по умолчанию не пробрасывает upgrade-заголовки, и wss-соединение молча умирает. В server-блок нужно добавить:
location /ws {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 3600;
}
Без proxy_read_timeout подлиннее соединение будет рваться каждые 60 секунд — в логах тихо, а пользователи жалуются, что «уведомления то есть, то нет».
TLS: certbot строго с плагином nginx
apt install -y certbot python3-certbot-nginx
certbot --nginx -d crm.example.ru
Наша грабля из практики: на одном клиентском сервере сертификат был выпущен через certbot certonly --standalone, и первое же автопродление через 60 дней провалилось — standalone-режим поднимает собственный веб-сервер на порту 80, который занят nginx. CRM встретила понедельник с протухшим сертификатом. С тех пор правило жёсткое: authenticator только nginx — он продлевает сертификат через работающий веб-сервер без остановки чего-либо. Проверяйте продление заранее командой certbot renew --dry-run.
SMTP для исходящей почты
Заходим в Administration → Outbound Emails и прописываем SMTP вашего почтового сервера: хост, порт, шифрование, логин ящика-отправителя. Без этого CRM не отправит ни приглашение новому пользователю, ни напоминание, ни письмо клиенту из карточки. Сразу же жмём кнопку тестовой отправки и проверяем, что письмо дошло и не упало в спам — если ваш домен без SPF/DKIM, начните с их настройки, иначе вся исходящая корреспонденция CRM поедет в «нежелательное».
Первичная настройка под РФ: язык, часовой пояс, форматы
Технически система работает, теперь делаем её удобной для российской компании. Всё перечисленное — штатные настройки бесплатного ядра, докупать ничего не нужно.
В Administration → Settings выставляем:
- Language — русский. Локализация встроена в ядро и обновляется вместе с релизами; после переключения весь интерфейс, включая названия сущностей, становится русским.
- Time Zone — Europe/Moscow (или ваш регион). Ошибётесь здесь — и все напоминания будут срабатывать со сдвигом, а в отчётах поплывут границы суток.
- Первый день недели — понедельник. Мелочь, но американское воскресенье в календаре раздражает менеджеров ежедневно.
- Формат даты DD.MM.YYYY и 24-часовое время — привычные глазу российские форматы.
- Валюта по умолчанию — RUB. Иначе все суммы сделок будут в долларах, и переделывать придётся руками по каждой записи.
Отдельный совет из внедренческой практики: команды (Teams) и роли создавайте до заведения пользователей. Порядок важен: при создании пользователя вы сразу привязываете его к команде и роли, и права применяются с первого входа. Если сделать наоборот — завести два десятка людей, а потом раздавать команды, — вы получите период, когда каждый видит все данные всех, и вычищать последствия (кто что успел посмотреть и отредактировать) сильно дороже, чем потратить полчаса на структуру заранее.
Проверяем, что всё живо: чек-лист приёмки
Перед передачей CRM клиенту мы прогоняем внутренний чек-лист сдачи из 12 пунктов. Делюсь его рабочей частью — прогоните по нему свою инсталляцию, даже если ставили для себя.
- Логи чистые. Смотрим
data/logs/за сегодня: файл должен быть пуст или содержать только INFO. Повторяющиеся ERROR — разбираться до передачи, а не после. - Cron-задания крутятся. Administration → Scheduled Jobs: у всех заданий свежее время последнего запуска (минуты назад, не «Never» и не вчера). Если «Never» — cron не подключён, возвращайтесь к пост-установке.
- Входящая почта. Подключаем IMAP-ящик, шлём на него тестовое письмо с личного адреса — через пару минут оно должно появиться в CRM и привязаться к карточке контакта с этим адресом.
- Исходящая почта. Отправляем письмо из карточки клиента — оно доходит до адресата и не падает в спам.
- Уведомления в реальном времени. Открываем CRM двумя пользователями, первым назначаем задачу второму — уведомление появляется без обновления страницы. Не появляется — чините WebSocket.
- Вложения. Загружаем в карточку файл размером с ваш реальный максимум (у нас — 45 МБ) и скачиваем обратно. Ошибка 413 — лимиты nginx и php.ini не согласованы.
- HTTPS и продление. Сертификат валиден,
certbot renew --dry-runпроходит без ошибок. - Служебные каталоги закрыты. Открываем в браузере
/data/config.php— должен быть отказ 403/404, а не содержимое файла. - Бэкап и восстановление. Снимаем дамп БД и архив файлов, разворачиваем на тестовой площадке, входим. Бэкап, из которого ни разу не восстанавливались, бэкапом не считается.
Последний пункт — нагрузочная прикидка. Для 50 рабочих мест мы гоняем по странице логина и паре типовых страниц лёгкий тест утилитой ab или siege на 20–30 одновременных соединений и смотрим, чтобы время ответа держалось в сотнях миллисекунд, а PHP-FPM не упирался в потолок процессов (в логе pool появятся предупреждения про max_children — тогда лимит вверх). Это не научный бенчмарк, но перегруженный сервер такой тест выявляет до того, как его выявят пользователи в понедельник утром.
Топ-7 граблей из нашей практики внедрений
Финальная выжимка — то, с чем мы реально сталкивались на инсталляциях EspoCRM у клиентов. Каждый пункт — сэкономленные вам часы.
- Права на data/ после работы под root. Классика: администратор что-то поправил на сервере из-под root, тот создал файлы в
data/cache, и CRM начала сыпать ошибками записи. Лечение:chown -R www-data:www-data /var/www/espocrm/data. Профилактика: все команды обслуживания запускать черезsudo -u www-data. - Opcache и «зависшие» изменения. После обновления или правки кода часть страниц живёт старой версией — PHP отдаёт закэшированные опкоды. Перезапустите php8.3-fpm после любого апдейта; мы включили это прямо в регламент обновления.
- Лимит размера письма. IMAP-забор большого письма с вложениями обрывается по memory_limit. Если у клиента ходят тяжёлые письма — поднимайте memory_limit выше рекомендованных 256M и следите за логами первых недель.
- Коллизия порта WebSocket. На сервере, где уже жил другой сервис с веб-сокетами, порт оказался занят — демон Espo молча не стартовал. Проверяйте
ss -tlnpдо запуска и разносите порты. - WAF хостера режет REST API. У одного клиента mod_security на стороне хостера блокировал PUT-запросы — интерфейс работал, а сохранение записей через API падало с 403. Диагностика заняла вечер; если API отвечает ошибками при живом интерфейсе — первым делом смотрите WAF и панель хостера.
- Устаревшие параметры mbstring в php.ini. На сервере, переехавшем со старого проекта, в php.ini остались экзотические переопределения mbstring (перегрузка строковых функций) — часть текста в карточках превращалась в кашу. На чистой Ubuntu 24.04 этого нет, но на «унаследованных» серверах проверяйте настройки mbstring до установки.
- Strict mode MariaDB. Жёсткие sql_mode-настройки, оставшиеся от прежнего проекта, роняли часть запросов при сохранении записей. Симптом — ошибки SQL в
data/logsпри нормально выглядящем интерфейсе. На типовой инсталляции с дефолтным конфигом MariaDB проблема не воспроизводится — ловите её именно на серверах с чужим наследством.
Что дальше: пользователи, роли и передача клиенту
Система установлена, проверена и настроена под РФ — остался последний рывок перед боевой эксплуатацией.
Заведение пользователей. Создаём учётки менеджеров, привязываем к командам и ролям, подготовленным на шаге первичной настройки. Для компаний с Active Directory есть опция LDAP-аутентификации (нужно PHP-расширение ldap из списка опциональных): один пароль на всё, учётки блокируются централизованно вместе с доменной — для наших клиентов с AD это стандарт.
Принцип минимальных прав. В ролях Espo область видимости настраивается на уровне own / team / all по каждой сущности, плюс есть ограничение доступа к отдельным полям. Наше правило: менеджер видит свою команду, руководитель — всё, право экспорта данных — только у руководителя. Право массового экспорта у рядового менеджера — это подарок самому себе на день его увольнения.
Передача клиенту. Мы сдаём инсталляцию пакетом: доступы администратора, документ с описанием окружения (версии, пути, расписание бэкапов), прогнанный чек-лист приёмки и короткая сессия обучения для того, кто будет администрировать систему на стороне клиента. Если администрировать некому — берём сервер на абонентское сопровождение: обновления, бэкапы, мониторинг.
На этом установка закончена, но жизнь CRM только начинается. В следующих статьях серии я разберу интеграции — телефонию, сайт и 1С через REST API и вебхуки, — миграцию данных из старой CRM или из таблиц, а также эксплуатацию: регламент обновлений через php command.php upgrade, стратегию бэкапов и мониторинг. А если разворачивать и сопровождать самостоятельно некогда — эту работу можно просто отдать нам.
Оставить комментарий