Разворачиваем Odoo 19 Community на своём сервере: от установки до продакшена

Архитектура продакшен-стенда Odoo 19: интернет через nginx с TLS на воркеры Odoo, за ними PostgreSQL, сбоку filestore и cron-воркер

Планирование железа: сколько ресурсов нужно Odoo 19

Меня зовут Евгений Семёнов, я технический директор ITfresh. Мы обслуживаем компании до 50 рабочих мест в Москве, держим собственные серверы в дата-центре МТС и арендуем VPS у российских провайдеров. Odoo я ставлю клиентам не первый год — и на арендованные виртуалки, и на свои гипервизоры в ЦОД. Эта статья — концентрат практики: реальные команды, рабочие конфиги, сайзинг из внедрений и грабли первых запусков, которых в официальной документации нет. Речь идёт про Odoo 19.0 — актуальный стабильный релиз, представленный на Odoo Experience в сентябре 2025 года; двадцатой версии на момент написания ещё не существует, так что вся конкретика ниже — про девятнадцатую.

Начинается любое внедрение с вопроса, который задаёт каждый заказчик: «какой сервер брать?». Ошибка тут стоит дорого в обе стороны. Недобор ресурсов даёт тормоза интерфейса и злых менеджеров в понедельник утром, перебор — переплату каждый месяц за простаивающие ядра. За несколько проектов у меня сложилась таблица, от которой я отталкиваюсь при заказе машины. Odoo 19 требует Python 3.12 и выше и PostgreSQL — на боевых стендах я ставлю PostgreSQL 16 как проверенную и стабильную ветку.

Три конфигурации сервера под Odoo 19 для 10, 25 и 50 пользователей: рост числа ядер, оперативной памяти и объёма диска
ПользователейvCPURAMДиск (SSD)Сценарий
до 10 (пилот)24 ГБот 40 ГБОбкатка: пара модулей, лёгкая нагрузка, немного одновременных сессий
10–252–44–8 ГБот 60 ГБЕжедневная работа отдела: продажи, склад, закупки
25–50 активных48 ГБот 80 ГБКомфорт: 30–50 одновременных сессий без задумчивости интерфейса

Пояснения к цифрам. На пилоте до десяти человек Odoo 19 уверенно живёт на двух ядрах и четырёх гигабайтах: этого хватает и приложению, и PostgreSQL, и системному запасу. Как только подключаются модули и растёт число одновременных сессий, я перехожу на четыре ядра и восемь гигабайт — это тот самый рабочий тариф, на котором отдел из 30–50 активных пользователей работает без подвисаний.

Почему диск считаем с запасом

Сорок гигабайт — это минимум, и вот из чего он складывается. Сама Ubuntu и рабочее окружение съедают несколько гигабайт. База PostgreSQL растёт по мере накопления заказов, писем и истории документов. Но главный пожиратель места — filestore: Odoo хранит вложения (сканы договоров, коммерческие предложения, фотографии товаров) не в базе, а отдельными файлами на диске, по умолчанию в /var/lib/odoo. Плюс локальные бэкапы за несколько последних дней. Отдел из 25 человек за год спокойно набирает 15–20 ГБ одних только вложений. Взять диск побольше сразу дешевле, чем через полгода расширять его на живой системе.

Где хостить и почему база с приложением на одном хосте — норма

Российский VPS против собственной виртуализации — вопрос масштаба. Для одного-двух клиентов проще арендовать VPS у отечественного провайдера: ориентир по деньгам на 2026 год — примерно 1500–3000 ₽ в месяц за конфигурацию под 25–50 пользователей. Когда клиентов много и есть свой парк, я разворачиваю Odoo на собственных гипервизорах в ЦОД МТС — так дешевле в пересчёте на стенд и полностью под нашим контролем. В обоих случаях действует правило: сервер держим в России. Причина не только в скорости отклика (европейский пинг в полтораста миллисекунд на каждый клик менеджеры замечают и жалуются), но и в 152-ФЗ — CRM и ERP с персональными данными по закону должны храниться на серверах в РФ.

И ещё один частый вопрос: не вынести ли PostgreSQL на отдельную машину? Для парка до 50 рабочих мест — не нужно. Приложение и база на одном хосте — это меньше сетевых задержек между Odoo и PostgreSQL, проще бэкап и мониторинг, дешевле железо. Разносить базу на выделенный сервер имеет смысл на сотнях пользователей и высокой конкурентной нагрузке; на нашем масштабе это переусложнение без выигрыша.

Вариант 1: нативная установка на Ubuntu 24.04

Способов поставить Odoo по сути два — нативно как системный сервис или в контейнерах. Оба рабочие, и у каждого своя область применения; ниже я разбираю сначала нативный путь, затем Docker, а в конце раздела объясняю, что и когда выбираю сам.

Сравнение двух путей установки Odoo 19: нативная через apt-пакет и через Docker-контейнер, с пометками продакшен и пилот

Нативная установка — классический путь: подключаем официальный репозиторий Odoo, ставим систему через apt как обычный сервис под systemd, а рядом поднимаем PostgreSQL из репозитория Ubuntu. Так вы получаете прозрачное Python-окружение и удобное резервное копирование средствами ОС. Берём свежий VPS с Ubuntu 24.04 LTS и работаем от sudo-пользователя.

PostgreSQL и роль odoo без прав суперпользователя

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

# ставим PostgreSQL 16 из репозитория
apt update && apt -y install postgresql postgresql-client

# создаём роль odoo: может логиниться и создавать БД, но НЕ суперпользователь
sudo -u postgres psql -c "CREATE ROLE odoo LOGIN CREATEDB PASSWORD 'сложный_пароль_БД';"

# проверяем, что роль появилась и права верные
sudo -u postgres psql -c "\du odoo"

Ключевые слова здесь: LOGIN разрешает роли подключаться, CREATEDB даёт право заводить базы (Odoo создаёт базу сам при инициализации), а отсутствие SUPERUSER — сознательное ограничение. Пароль в кавычках должен совпасть с тем, что мы пропишем в конфиге Odoo.

Установка Odoo 19 из официального apt-репозитория

Odoo публикует собственный deb-репозиторий с готовыми пакетами. Подключаем его ключ и источник, затем ставим пакет odoo — он тянет все зависимости и заводит systemd-сервис:

# ключ репозитория Odoo
wget -qO - https://nightly.odoo.com/odoo.key | gpg --dearmor \
  -o /usr/share/keyrings/odoo-archive-keyring.gpg

# источник для версии 19.0
echo 'deb [signed-by=/usr/share/keyrings/odoo-archive-keyring.gpg] \
  https://nightly.odoo.com/19.0/nightly/deb/ ./' \
  > /etc/apt/sources.list.d/odoo.list

apt update
apt -y install odoo

# сервис уже поднят и добавлен в автозапуск
systemctl status odoo

Пакет разворачивает стандартную структуру: исполняемые файлы в системных путях, юнит odoo.service, каталог кода и — что нам важнее всего — конфиг /etc/odoo/odoo.conf. Именно его мы дальше правим под прод.

Структура /etc/odoo/odoo.conf

Открываем /etc/odoo/odoo.conf и приводим к боевому виду. Разберу каждый параметр после листинга:

[options]
; master-пароль менеджера баз данных — обязательно сменить и спрятать!
admin_passwd = очень_длинный_случайный_мастер_пароль
; подключение к PostgreSQL
db_host = localhost
db_port = 5432
db_user = odoo
db_password = сложный_пароль_БД
; в проде показываем только свою базу и запрещаем менеджер БД
dbfilter = ^ваша_база$
list_db = False
; пути к модулям: штатные + свои
addons_path = /usr/lib/python3/dist-packages/odoo/addons,/opt/odoo/custom-addons
; за реверс-прокси nginx — доверяем заголовкам X-Forwarded-*
proxy_mode = True
; воркеры и лимиты (подробный тюнинг — в разделе ниже)
workers = 5
max_cron_threads = 1
limit_memory_soft = 2147483648
limit_memory_hard = 2684354560
limit_time_cpu = 600
limit_time_real = 1200
; куда писать лог
logfile = /var/log/odoo/odoo.log

Что здесь критично. admin_passwd — мастер-пароль менеджера баз данных, а не пароль администратора Odoo; о нём отдельно ниже. db_host = localhost, db_user = odoo и db_password — те самые реквизиты роли, что мы создали в PostgreSQL. addons_path перечисляет через запятую каталоги, где Odoo ищет модули: сначала штатные, потом наш каталог кастомных дополнений. proxy_mode = True включаем потому, что перед Odoo будет стоять nginx.

Первый запуск, создание базы и мастер-пароль

Перезапускаем сервис, чтобы применился конфиг, и смотрим лог: systemctl restart odoo и journalctl -u odoo -f. Odoo по умолчанию слушает HTTP на порту 8069. Пока list_db ещё не мешает первичной инициализации, заходим через будущий nginx и создаём базу — мастер создания спросит четыре вещи:

  • Master Password — тот самый admin_passwd из конфига: он авторизует операции с базами.
  • Database Name — имя боевой базы; оно же попадёт в dbfilter.
  • Email и Password — логин и пароль первого администратора Odoo (учётной записи внутри системы).
  • Language и Country — сразу ставим русский и Россию, чтобы справочники и форматы дат подтянулись корректно.

После создания базы возвращаемся в odoo.conf, убеждаемся, что list_db = False и dbfilter прописаны на нашу единственную базу, и перезапускаем сервис. Теперь менеджер баз снаружи закрыт, а боевая база заперта от посторонних.

Мастер-пароль и list_db — самое опасное место установки. Значение admin_passwd по умолчанию — admin. Это открытая дверь: тот, кто знает мастер-пароль, через веб может создать, удалить, скачать или восстановить ЛЮБУЮ базу на сервере. Всегда меняем его на длинную случайную строку и кладём в менеджер паролей. А list_db = False выключает страницу менеджера БД и выпадающий список баз на боевом сайте — без этого случайный посетитель видит имена ваших баз и форму управления ими. Эти два параметра — не «желательно», а обязательный минимум перед выходом в прод.

Вариант 2: развёртывание через Docker Compose

Второй путь — официальный образ. Odoo публикует контейнер odoo:19 (это ровно Community-редакция), а базу поднимают отдельным контейнером postgres:16. Мы описываем весь стек одним файлом docker-compose.yml — вот он целиком, с разбором ниже:

services:
  db:
    image: postgres:16
    environment:
      POSTGRES_DB: postgres
      POSTGRES_USER: odoo
      POSTGRES_PASSWORD: сложный_пароль_БД
    volumes:
      - odoo-db-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "odoo"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: unless-stopped

  web:
    image: odoo:19
    depends_on:
      db:
        condition: service_healthy
    ports:
      - "127.0.0.1:8069:8069"
      - "127.0.0.1:8072:8072"
    environment:
      HOST: db
      USER: odoo
      PASSWORD: сложный_пароль_БД
    volumes:
      - odoo-web-data:/var/lib/odoo
      - ./config:/etc/odoo
      - ./addons:/mnt/extra-addons
    restart: unless-stopped

volumes:
  odoo-db-data:
  odoo-web-data:

Разберу построчно то, где легко наступить на грабли:

  • Сервис db на образе postgres:16. Переменные POSTGRES_USER и POSTGRES_PASSWORD задают учётку, под которой Odoo ходит в базу; они обязаны совпадать с USER и PASSWORD сервиса web. Том odoo-db-data хранит данные PostgreSQL — это то, что вы бэкапите и что переживает пересоздание контейнера.
  • healthcheck на pg_isready. Без него web-контейнер при старте может ломануться в ещё не поднявшуюся базу и упасть. Связка depends_on с condition: service_healthy заставляет приложение дождаться, пока PostgreSQL реально готов принимать соединения, а не просто запущен.
  • Порты только на localhost. Запись 127.0.0.1:8069:8069 публикует порт исключительно на петлевом интерфейсе — снаружи он недоступен, наружу смотрит только nginx. Порт 8072 — gevent-воркер под живой чат и веб-сокеты, его тоже пробрасываем на localhost.
  • Тома filestore и addons. odoo-web-data:/var/lib/odoo — тот самый filestore с вложениями, его теряют чаще всего. Каталог ./config отдаём под odoo.conf, а ./addons монтируем в /mnt/extra-addons под свои и сторонние модули.
  • restart: unless-stopped. Стек сам поднимется после перезагрузки сервера или сбоя контейнера — без этого после ночного ребута VPS клиент утром встречает мёртвую систему.

Переменные окружения HOST, USER, PASSWORD в сервисе web — это способ образа odoo:19 узнать реквизиты базы без правки конфига. Тонкий тюнинг (воркеры, лимиты, proxy_mode, list_db) кладём в ./config/odoo.conf — точно такой же по смыслу, как в нативном варианте, только db_host = db (имя сервиса из compose). Поднимаем стек командой docker compose up -d из каталога проекта и смотрим логи через docker compose logs -f web.

Когда Docker, а когда нативная установка

Выбор между двумя способами у меня сводится к простому вопросу — что важнее в конкретном проекте.

  • Docker берём для быстрых пилотов и мультиинстанса. Когда нужно за час поднять стенд на демонстрацию, или когда на одном хосте живёт несколько независимых инстансов Odoo (разные клиенты, dev и prod рядом) — контейнеры вне конкуренции. Одинаковая среда везде, обновление сменой тега образа, откат — обратной сменой тега, данные в отдельных томах никуда не деваются.
  • Нативную ставим под прод с бэкапами средствами ОС. Когда стенд один, живёт долго и его надо бесшовно вписать в существующую инфраструктуру резервного копирования и мониторинга уровня операционной системы, нативная установка прозрачнее: сервис под systemd, логи в привычных путях, pg_dump и архивы filestore снимаются штатными инструментами хоста.

На практике для клиентов до 50 рабочих мест я чаще беру Docker ради предсказуемости, а нативную оставляю там, где заказчик уже живёт в устоявшейся серверной среде со своими регламентами бэкапа. Оба варианта дальше закрываю одинаково — реверс-прокси nginx с TLS.

Продакшен-обвязка: nginx, TLS и websocket

Odoo напрямую наружу не выставляют никогда. Перед ним ставят nginx, который терминирует TLS, отдаёт статику быстрее и защищает приложение. И здесь же кроется грабля, которая портит жизнь чаще всего, — веб-сокеты живых уведомлений.

Два апстрима: обычный трафик и websocket

Идея простая: основной поток запросов nginx проксирует на порт 8069, а всё, что идёт по пути /websocket, — на отдельный порт 8072, где живёт gevent-воркер. Этот воркер держит живой чат, всплывающие уведомления и шину обмена сообщениями. Вот боевой конфиг целиком:

upstream odoo {
    server 127.0.0.1:8069;
}
upstream odoochat {
    server 127.0.0.1:8072;
}

# карта для корректного апгрейда соединения до websocket
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

server {
    listen 80;
    server_name erp.example.ru;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name erp.example.ru;

    ssl_certificate     /etc/letsencrypt/live/erp.example.ru/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/erp.example.ru/privkey.pem;

    # сжатие текстовых ответов
    gzip on;
    gzip_types text/css text/plain application/javascript application/json image/svg+xml;

    # запас под вложения: сканы, КП, картинки товаров
    client_max_body_size 100m;

    proxy_read_timeout 720s;
    proxy_connect_timeout 720s;
    proxy_send_timeout 720s;

    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 на порт 8072
    location /websocket {
        proxy_pass http://odoochat;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    # весь остальной трафик — на порт 8069
    location / {
        proxy_pass http://odoo;
        proxy_redirect off;
    }

    # статика с кэшированием
    location ~* /web/static/ {
        proxy_cache_valid 200 90m;
        proxy_buffering on;
        expires 864000;
        proxy_pass http://odoo;
    }
}

Заголовки X-Forwarded-* — не косметика. X-Real-IP и X-Forwarded-For передают в Odoo реальный адрес клиента (иначе в логах будет только адрес nginx), а X-Forwarded-Proto $scheme сообщает, что снаружи соединение зашифровано. Именно ради корректной обработки этих заголовков в конфиге Odoo включён proxy_mode = True — без него ссылки в письмах и редиректы будут кривыми, а система решит, что работает по незащищённому http.

Грабля номер один: /websocket, а не /longpolling. Начиная с Odoo 16 живые уведомления и чат работают через веб-сокет по пути /websocket на порту 8072 — при условии, что включено несколько воркеров. Старый путь /longpolling — устаревший механизм, живший до 16-й версии, и в Odoo 19 проксировать его бессмысленно. Половина инструкций в сети до сих пор описывает longpolling — не копируйте их вслепую. Если забыть блок location /websocket или заголовки Upgrade и Connection, интерфейс внешне работает, но уведомления и чат «зависают»: сообщения не прилетают в реальном времени, пока пользователь не обновит страницу. Диагноз ставится по вкладке Network в браузере — там видно, как соединение к /websocket обрывается.

HTTPS через certbot с плагином nginx

Сертификат Let's Encrypt выпускаем плагином nginx — он сам правит конфиг и корректно продлевается потом:

apt -y install certbot python3-certbot-nginx
certbot --nginx -d erp.example.ru

# обязательная проверка автопродления
certbot renew --dry-run

Классическая ошибка — выпускать сертификат режимом certbot certonly --standalone: он поднимает собственный веб-сервер на 80-м порту, а тот уже занят nginx. В лучшем случае команда падает, в худшем — вы гасите nginx ради выпуска, забываете, и через 60 дней автопродление молча проваливается по той же причине. Плагин --nginx проходит проверку через уже работающий сервер, ничего не останавливая. Сжатие gzip ощутимо ускоряет загрузку интерфейса (Odoo отдаёт много CSS и JavaScript), а client_max_body_size обязан быть не меньше вашего реального максимального вложения — иначе nginx завернёт тяжёлый скан ошибкой 413 ещё до того, как запрос дойдёт до Odoo.

Тюнинг под нагрузку: воркеры, память, longpolling

Свежепоставленный Odoo из коробки работает в один поток — для боевого отдела это неприемлемо. Разберём, как перевести систему в многопроцессный режим и не уронить её лимитами.

Почему workers=0 нельзя на 20+ пользователях

По умолчанию Odoo запускается с workers = 0 — это однопроцессный, многопоточный режим, задуманный для разработки. У него два системных изъяна для прода. Во-первых, в нём не поднимается отдельный gevent-longpolling сервер на порту 8072, а значит толком не работают веб-сокеты и живые уведомления. Во-вторых, вся нагрузка ложится на единственный процесс: два менеджера, открывшие тяжёлый отчёт одновременно, уже чувствуют тормоза, а на двадцати активных пользователях интерфейс начинает ощутимо подвисать — типовой симптом, с которым к нам приходят «Odoo тормозит, хотя сервер мощный». Диагноз почти всегда один: забыли включить воркеры.

Боевой режим — многопроцессный. Число рабочих воркеров считаем по эмпирической формуле:

workers = (число vCPU * 2) + 1

Для машины на два ядра это 5 воркеров, на четыре — 9. Как только workers > 0, Odoo автоматически поднимает gevent-сервер longpolling на порту 8072 — тот самый, на который nginx проксирует /websocket. Отдельно задаём max_cron_threads — потоки под плановые задачи (рассылки, напоминания, автоматизации); обычно хватает одного, максимум двух. Итого на двухъядерном VPS: workers = 5, max_cron_threads = 1.

Лимиты памяти и времени на воркер

Каждый воркер надо ограничить, иначе один разросшийся процесс съест RAM и уронит сервер. Odoo сам мягко перезапускает воркер, когда тот переходит мягкий лимит после обработки текущего запроса, и убивает жёстко при превышении верхней границы. Плюс лимиты по времени защищают от зависших запросов. Мои рабочие значения для старта на четырёх гигабайтах:

workers = 5                      ; для 2 vCPU: 2*2+1
max_cron_threads = 1             ; поток под плановые задачи
limit_memory_soft = 2147483648   ; ~2 ГБ — мягкий лимит на воркер
limit_memory_hard = 2684354560   ; ~2.5 ГБ — жёсткий лимит на воркер
limit_time_cpu = 600             ; предел процессорного времени на запрос, сек
limit_time_real = 1200           ; предел реального времени на запрос, сек

Здравый смысл при подборе числа воркеров: посчитайте, сколько памяти они суммарно займут по мягкому лимиту, и оставьте запас под PostgreSQL и систему. На восьмигигабайтной машине с четырьмя ядрами девять воркеров по два гигабайта в пике — уже впритык, поэтому на таких конфигурациях я либо снижаю мягкий лимит, либо не гонюсь за максимальным числом воркеров, а балансирую под реальную нагрузку. limit_time_real держим заметно выше limit_time_cpu, потому что реальное время включает ожидание базы и сети, а не только счёт процессора.

Проверка после включения воркеров. Убедитесь, что порт 8072 действительно слушается: ss -ltnp | grep 8072 (в Docker — внутри контейнера web). Если порт пуст, а workers при этом больше нуля — значит конфиг не применился. Второй тест — открыть систему двумя пользователями и назначить одному задачу от другого: уведомление обязано прилететь без обновления страницы. Прилетело — websocket и воркеры настроены верно.

Базовая настройка после установки: чек-лист безопасного старта

Система развёрнута и защищена — остаётся сделать из голого Odoo рабочий инструмент и закрыть последние дыры. Вот последовательность, по которой я готовлю стенд к передаче.

Русский язык, компания и реквизиты

Если русский не подхватился на этапе создания базы, идём в «Настройки» → «Перевод» → «Языки», загружаем русский и назначаем его пользователям; заодно выставляем в настройках пользователя формат даты и часовой пояс Москвы. Дальше заполняем карточку компании: название, ИНН/КПП, адрес, банковские реквизиты, логотип. Это не косметика — эти данные подставляются в счета, коммерческие предложения и другие печатные формы, поэтому лучше внести их один раз и правильно.

Стартовые модули

Ставим только то, что нужно с первого дня, — в разделе «Приложения». Типовой набор для торговой компании:

  • Продажи (Sales) — заказы, коммерческие предложения, прайс-листы.
  • Склад (Inventory) — приход, отгрузка, остатки, если компания работает с товаром.
  • CRM — воронка лидов и сделок для отдела продаж.
  • Закупки (Purchase) — заказы поставщикам и пополнение склада.

Остальное подключаем осознанно, когда бизнес-процесс дозреет, а не потому что система предложила. Чем меньше в интерфейсе кнопок, ведущих не туда, тем быстрее сотрудники осваиваются.

Отключаем регистрацию и закрываем менеджер БД

Два действия, без которых нельзя выпускать стенд в интернет. Первое — отключить свободную регистрацию с сайта: в «Настройки» → «Основные настройки» выставляем режим, при котором аккаунты заводит только администратор (Free sign up выключен). Иначе любой посетитель заведёт себе учётку в вашей ERP. Второе — ещё раз убедиться, что в odoo.conf стоит list_db = False и dbfilter ограничивает сервер единственной базой. Проверяем прямо в браузере: страница менеджера баз данных должна быть недоступна, а не показывать форму со списком.

Первые грабли и как мы их обходим

Соберу в одном месте пять проблем, которые вылезают у тех, кто ставит Odoo впервые. Все они простые, но каждая способна испортить первый рабочий день на глазах у всего отдела.

Незакрытый мастер-пароль менеджера БД

Самая частая и самая опасная. Стенд ставят, базу создают, а admin_passwd так и остаётся дефолтным admin, да ещё и list_db забывают выключить. В итоге любой, кто знает адрес, открывает менеджер баз, видит список баз и одним нажатием может скачать дамп со всеми данными клиентов. Лечится одной строкой в конфиге и перезапуском — но проверять это надо обязательно, а не «на глаз».

Longpolling и websocket за прокси

Второе по частоте: уведомления и чат не приходят в реальном времени. Причина — либо воркеры не включены (при workers = 0 порт 8072 не поднимается вовсе), либо в nginx нет блока location /websocket с заголовками апгрейда. Проверяем оба конца: слушается ли 8072 и проксирует ли на него nginx. Ошибка усугубляется тем, что интерфейс при этом внешне работает — проблему замечают не сразу.

Локаль PostgreSQL

Третья грабля тоньше. Если база PostgreSQL создана в неподходящей локали, ломается сортировка кириллицы и форматы. При нативной установке лечится тем, что базу создаёт сам Odoo — он делает это корректно. В Docker образ postgres:16 инициализирует кластер в UTF-8, чего достаточно; но если вы вручную создавали базу с явной локалью, убедитесь, что это UTF-8, иначе русские названия будут сортироваться неправильно.

Обновление минорных версий: apt против пересборки контейнера

Обновляться в пределах 19.0 надо, и делается это по-разному в зависимости от способа установки. В нативном варианте — apt update && apt install --only-upgrade odoo с последующим перезапуском сервиса. В Docker — вытянуть свежий образ (docker compose pull) и пересоздать контейнер (docker compose up -d); данные в томах при этом не трогаются. В обоих случаях перед обновлением делаем бэкап: минорные апдейты обычно проходят гладко, но правило «сначала бэкап, потом обновление» не отменяется никогда.

Бэкап: pg_dump плюс filestore

Полный бэкап Odoo — это две части, и потеря любой делает копию бесполезной. Первая — дамп базы через pg_dump. Вторая — архив filestore (каталог /var/lib/odoo или том odoo-web-data), где лежат все вложения. Дамп базы без filestore восстановит структуру и записи, но все сканы, КП и картинки товаров будут битыми ссылками.

# нативная установка
sudo -u postgres pg_dump -Fc ваша_база > /backup/odoo_$(date +%F).dump
tar czf /backup/filestore_$(date +%F).tgz /var/lib/odoo/filestore/ваша_база

# Docker: дамп из контейнера db и архив тома filestore
docker compose exec -T db pg_dump -U odoo -Fc ваша_база > /backup/odoo_$(date +%F).dump
docker run --rm -v odoo-web-data:/data -v /backup:/backup alpine \
  tar czf /backup/filestore_$(date +%F).tgz -C /data filestore
Бэкап, из которого ни разу не восстанавливались, — не бэкап. Регулярно разворачивайте копию на тестовой базе и заходите внутрь: убеждайтесь, что и записи, и вложения на месте. Файл, о котором вы думаете, что он вас спасёт, — это ещё не спасение. Настоящая проверка — успешное восстановление, а не наличие файла на диске.

Итог: чек-лист развёртывания из 12 пунктов

Соберём всё сказанное в короткий список, по которому я прогоняю каждую инсталляцию перед передачей клиенту. Если все двенадцать пунктов зелёные — стенд можно выпускать в бой.

  1. Сервер по сайзингу. Ресурсы подобраны под число пользователей (2 vCPU / 4 ГБ на пилот, 4 vCPU / 8 ГБ на 30–50 активных), диск взят с запасом под filestore.
  2. Хостинг в России. VPS или своя виртуализация в РФ — по 152-ФЗ и ради скорости отклика.
  3. PostgreSQL 16, роль без суперпользователя. Роль odoo создана с LOGIN CREATEDB, но без SUPERUSER.
  4. Способ установки выбран осознанно. Docker для пилотов и мультиинстанса, нативная — под прод с бэкапами средствами ОС.
  5. Мастер-пароль сменён и в сейфе. admin_passwd — длинная случайная строка, а не admin.
  6. list_db выключен, dbfilter настроен. Менеджер баз данных снаружи недоступен, сервер обслуживает единственную базу.
  7. proxy_mode = True. Odoo доверяет заголовкам nginx, ссылки и протокол корректны.
  8. nginx с двумя апстримами. Основной трафик на 8069, /websocket на 8072, заголовки X-Forwarded-* проставлены.
  9. HTTPS с автопродлением. Сертификат Let's Encrypt выпущен плагином nginx, certbot renew --dry-run проходит без ошибок.
  10. Воркеры включены. workers = 2*CPU+1, лимиты памяти и времени выставлены, порт 8072 слушается.
  11. Базовая настройка сделана. Русский язык, реквизиты компании, стартовые модули, регистрация с сайта отключена.
  12. Бэкап снят и восстановлен. Дамп базы плюс архив filestore развёрнуты на тестовой базе и проверены заходом внутрь.

Что дальше. Эта статья — про развёртывание; в серии есть ещё два материала. Если вы ещё выбираете, стоит ли вообще ставить Odoo Community и чем она отличается от платной редакции, прочитайте честный разбор бесплатной ERP. А про то, как приземлить Odoo на российские реалии — НДС, печатные формы, обмен с 1С, — отдельный материал про локализацию и интеграцию с 1С.

И если разворачивать и сопровождать Odoo самостоятельно некогда или некому — это ровно наша работа. Мы поднимем стенд под ключ, закроем все двенадцать пунктов чек-листа и возьмём систему на сопровождение: обновления, бэкапы, мониторинг.

Развернём Odoo 19 на вашем сервере под ключ

ITfresh — IT-аутсорсинг для компаний до 50 рабочих мест в Москве. Развернём Odoo 19 Community на вашем VPS или на наших серверах в дата-центре МТС: PostgreSQL, nginx с корректным websocket и TLS, тюнинг воркеров под нагрузку, базовая настройка и чек-лист приёмки из 12 пунктов. Дальше возьмём на сопровождение — обновления, бэкапы, мониторинг. Telegram: @ITfresh_Boss, телефон: +7 903 729-62-41

📞 Связаться с нами
#установка Odoo #Odoo 19 Ubuntu #Odoo docker compose #Odoo nginx reverse proxy #развёртывание Odoo Community #odoo.conf workers #Odoo своими руками
Комментарии 0

Оставить комментарий

загрузка...

Подпишитесь на рассылку ITfresh

Раз в неделю — практические гайды для руководителя IT и сисадмина: безопасность, 1С, миграции, резервные копии, лайфхаки из реальных проектов.

Реквизиты оператора персональных данных

ООО «АЙТИ-ФРЕШ», ИНН 7719418495, КПП 771901001. Юридический адрес: 105523, г. Москва, Щёлковское шоссе, д. 92, корп. 7. Контакт: info@itfresh.ru, +7 903 729-62-41. Оператор обрабатывает e-mail подписчика в целях рассылки информационных и рекламных материалов до момента отзыва согласия.