SSD Nodes Learn Hosting plans →
Посібники Matt ConnorВід Matt Connor · Оновлено 2026-08-28

Як розгорнути ERPNext на VPS за допомогою Docker

Покрокове розгортання ERPNext на власному VPS: 11 контейнерів, TLS, вихідна пошта, фіксація версій і перевірене відновлення резервної копії.

Що ви беретеся запускати

Self-hosting ERPNext на VPS — це операційна задача, а не встановлення однією командою. Офіційний стек Docker Compose складається з одинадцяти контейнерів і містить вашу головну книгу та записи клієнтів. Тому до всього нижче потрібно ставитися особливо уважно: резервна копія не є резервною копією, доки ви не відновили її, а тег образу без фіксації версії — це міграція схеми, яка лише чекає на запуск.

У тексті часто використовуються кілька назв. ERPNext — це бізнес-застосунок. Frappe — Python-фреймворк, на якому він працює. Bench — інструмент командного рядка для керування сайтами, уже встановлений усередині контейнерів. Сайт — це один tenant: одна база даних MariaDB і один каталог завантажених файлів. Майже кожна команда тут виконується bench у контейнері backend для одного сайту з указаною назвою.

У цьому посібнику використовується репозиторій frappe_docker — саме це розгортання підтримує проєкт. Усі наведені нижче команди перевірено для цього репозиторію в August 2026. Якщо Docker Compose для вас новий, у матеріалі запуск Docker Compose на VPS описано базові відомості, на які спирається цей посібник.

Скільки ресурсів VPS потрібно ERPNext?

ChartCommon published ERPNext sizing tiers (guidance, not a measurement)
The data behind this chart
[
  {
    "label": "Evaluation",
    "vcpu": 2,
    "ram_gb": 4,
    "disk_gb": 40
  },
  {
    "label": "Small production",
    "vcpu": 4,
    "ram_gb": 8,
    "disk_gb": 100
  },
  {
    "label": "Room to grow",
    "vcpu": 4,
    "ram_gb": 16,
    "disk_gb": 160
  }
]

Опубліковані рекомендації починаються з 2 vCPU і 4 GB RAM ще до входу першого користувача. Це рівень для оцінювання. Це початкові значення, а не вимірювання з цього посібника. Фактичний обсяг визначає кількість документів у вашій системі. Останній рядок взагалі не є опублікованим мінімумом. Це приблизний рівень, після якого пам’ять перестає бути ресурсом, про який потрібно постійно думати.

Реалістично оцінюйте малі плани. VPS із 1 GB або 2 GB RAM запустить стек, але завершить роботу під час першого імпорту або першого тривалого звіту. Дев’ять контейнерів із тривалими процесами, buffer pool MariaDB і Python worker, який формує звіт, не вміщаються в такий обсяг пам’яті. Завершення роботи не буде коректним. Kernel out-of-memory killer зупинить контейнер, а docker inspect у ньому після цього покаже "OOMKilled": true з кодом завершення 137. Якщо worker завершено під час виконання завдання, поданий документ може залишитися з незавершеною фоновою обробкою.

Для компанії, яка щодня використовує ERPNext, чесним мінімумом є 8 GB RAM, 4 vCPU і 100 GB SSD. RAM закінчується першою. Диск заповнюється швидше, ніж очікують користувачі, оскільки кожне вкладення та кожна локальна резервна копія зберігаються на тому самому томі, що й база даних.

Одинадцять контейнерів і призначення кожного

Запустіть docker compose ps після запуску стека, коли працюють дев’ять контейнерів. Ще два, configurator і create-site, виконують свою роботу один раз і завершуються. Саме тому загальна кількість становить одинадцять.

  • backend запускає застосунок Frappe через gunicorn. Саме тут працює bench.
  • frontend — це nginx. Він обслуговує статичні ресурси, а всі інші запити передає бекенду.
  • queue-short і queue-long — це робочі процеси RQ (Redis Queue). Вони виконують фонові завдання, зокрема надсилання електронної пошти, імпорт і формування звітів.
  • scheduler запускає завдання за розкладом, зокрема заплановані звіти та документи з автоматичним повторенням.
  • websocket — це процес socket.io, який забезпечує оновлення даних у браузері в реальному часі.
  • db — це MariaDB.
  • redis-cache і redis-queue — два окремі екземпляри Redis: один для кешу, інший для черги завдань.

Цей поділ варто запам’ятати, оскільки він підказує, який журнал потрібно читати. Якщо електронний лист завис, проблема пов’язана з робочим процесом черги, тому docker compose logs -f queue-short — правильна команда. Якщо сторінка завантажується, але індикатор сповіщень не оновлюється, проблема пов’язана з websocket. Читання журналів backend у будь-якому з цих випадків лише марнує час.

Встановлюйте production compose-файли, а не demo

Репозиторій містить pwd.yml, і в README прямо зазначено: "This setup is intended for short-lived evaluation only. You will not be able to install custom apps to this setup." Використовуйте його, щоб ознайомитися з ERPNext протягом одного дня. Не запускайте на ньому систему для роботи компанії.

sudo apt update && sudo apt install -y git
curl -fsSL https://get.docker.com | bash
git clone https://github.com/frappe/frappe_docker
cd frappe_docker
mkdir -p ~/gitops
cp example.env ~/gitops/erpnext.env

Відкрийте ~/gitops/erpnext.env і змініть чотири значення. ERPNEXT_VERSION фіксує тег образу. У прикладі файлу DB_PASSWORD має значення 123. SITES_RULE — це правило маршрутизації Traefik, а LETSENCRYPT_EMAIL отримує попередження про сертифікат.

ERPNEXT_VERSION=v16.32.1
DB_PASSWORD=<a long random password>
SITES_RULE=Host(`erp.example.com`)
LETSENCRYPT_EMAIL=ops@example.com

Тепер згенеруйте один compose-файл і запустіть його.

docker compose --project-name erpnext \
  --env-file ~/gitops/erpnext.env \
  -f compose.yaml \
  -f overrides/compose.mariadb.yaml \
  -f overrides/compose.redis.yaml \
  -f overrides/compose.https.yaml \
  config > ~/gitops/erpnext.yaml

docker compose --project-name erpnext -f ~/gitops/erpnext.yaml up -d

config нічого не запускає. Він об’єднує базовий файл із файлами перевизначень і виводить результат, уже підставивши всі змінні. Потім запустіть згенерований файл. Цей додатковий крок виправданий: запущений стек описано в одному файлі, який можна прочитати й додати до репозиторію, тому його вміст не зміниться непомітно, якщо хтось відредагує env-файл або ви оновите репозиторій. У матеріалі як об’єднуються кілька файлів Docker Compose докладно пояснено правила перевизначення.

Дочекайтеся запуску db і завершення роботи configurator. Це займає кілька секунд. Потім створіть site.

docker compose --project-name erpnext exec backend \
  bench new-site --mariadb-user-host-login-scope=% \
  --db-root-password '<your DB_PASSWORD>' \
  --install-app erpnext \
  --admin-password '<a strong admin password>' \
  erp.example.com

Перевірте результат:

docker compose --project-name erpnext ps
docker compose --project-name erpnext exec backend bench --site erp.example.com list-apps

list-apps має вивести frappe і erpnext із зазначенням їхніх версій. Справний ps показує дев’ять сервісів у стані running і жодного — у стані restarting.

Тут часто виникають дві проблеми. --mariadb-user-host-login-scope=% не є необов’язковим у Docker. Контейнер застосунку підключається до MariaDB через Docker network, тому для бази даних він є віддаленим хостом, а користувач бази даних із дозволом лише для localhost не може підключитися з цього хоста. Після цього створення site завершується помилкою доступу MariaDB, у якій зазначено користувача root. Область % надає користувачу нового site доступ із будь-якого хоста в цій приватній мережі.

Друга проблема — ім’я site. За замовчуванням frontend вибирає site для обслуговування за значенням HTTP-заголовка Host, тому site, створений як erpnext, недоступний за адресою erp.example.com, хоча обидва значення існують. Назвіть site відповідно до домену, як показано вище, або задайте FRAPPE_SITE_NAME_HEADER в env-файлі як ім’я site і знову згенеруйте compose-файл.

HTTPS і що має бути виконано до початку роботи

Файл compose.https.yaml запускає Traefik на порту 443, перенаправляє порт 80 на нього та запитує сертифікати в Let's Encrypt. TLS (безпека транспортного рівня) захищає рахунок і cookie сеансу від передавання мережею у відкритому вигляді.

Мають виконуватися дві умови, інакше сертифікат не буде видано. A-запис DNS для erp.example.com уже має вказувати на VPS. Порти 80 і 443 мають бути доступні з інтернету, оскільки Let's Encrypt перевіряє, що ви керуєте цим доменним ім’ям, за допомогою HTTP-01 challenge на порту 80. Перевірте мережевий firewall у вашого провайдера, а також firewall на сервері. Це окремі засоби керування, і про firewall у панелі керування часто забувають.

Сертифікати зберігаються у volume cert-data за шляхом /letsencrypt/acme.json. Якщо браузер показує сертифікат за замовчуванням замість вашого, знайдіть назву proxy-сервісу у docker compose --project-name erpnext ps та перегляньте його журнали на наявність помилки ACME (автоматизоване керування сертифікатами). Запускаєте інші вебзастосунки на тому самому сервері? У матеріалі один екземпляр Traefik перед кількома застосунками Docker Compose показано, як спільно використовувати proxy, не намагаючись одночасно зайняти порт 443. Другий застосунок на такому сервері часто призначений для роботи з клієнтами, а self-hosted Chatwoot як help desk для підтримки працює за тим самим proxy. Тому працівники, які обробляють рахунки, можуть відповідати на клієнтські email і повідомлення в чаті в одному місці.

Вихідна електронна пошта, інакше рахунки не залишатимуть сервер

Цей крок пропускає більшість посібників з ERPNext, хоча саме він визначає, чи буде система корисною. Без налаштованої вихідної пошти клієнт не отримає рахунок, лист для скидання пароля не надійде, а запланований звіт не буде доставлений. У стеку немає поштового сервера.

Не намагайтеся надсилати пошту безпосередньо з VPS через порт 25. Більшість провайдерів блокують вихідний порт 25 для нових облікових записів. Навіть повідомлення, які проходять, відхиляються або потрапляють у спам, оскільки свіжа адреса VPS не має репутації відправника. Використовуйте автентифікований relay через порт 587.

Підтримуваний спосіб — екран Email Account в інтерфейсі ERPNext. Пароль у ньому зберігається в зашифрованому вигляді. Також можна записати параметри в конфігурацію сайту:

docker compose --project-name erpnext exec backend \
  bench --site erp.example.com set-config mail_server smtp.example.com

docker compose --project-name erpnext exec backend \
  bench --site erp.example.com set-config mail_port 587 --parse

docker compose --project-name erpnext exec backend \
  bench --site erp.example.com set-config use_tls 1 --parse

docker compose --project-name erpnext exec backend \
  bench --site erp.example.com set-config mail_login 'erp@example.com'

docker compose --project-name erpnext exec backend \
  bench --site erp.example.com set-config auto_email_id 'erp@example.com'

--parse зберігає 587 як число, а не як рядок "587". Прочитайте файл і переконайтеся, що навколо цих двох значень немає лапок:

docker compose --project-name erpnext exec backend \
  cat sites/erp.example.com/site_config.json

Налаштовуйте mail_password через екран Email Account, а не в командному рядку. Так значення зберігатиметься в зашифрованому вигляді й ніколи не потрапить до історії shell.

Потім надішліть реальне повідомлення. Створіть Sales Invoice, надішліть його на адресу, до якої маєте доступ, і під час цього стежте за чергою:

docker compose --project-name erpnext logs -f queue-short

Вихідна пошта — це фонова задача. Тому повідомлення, яке не надійшло, зазвичай відображається як невдала задача в цьому журналі, а не як помилка в браузері. Також опублікуйте записи SPF (sender policy framework) і DKIM (domainkeys identified mail) для домену відправника, а потім додайте політику DMARC. Без них навіть технічно коректний рахунок може потрапити до спаму клієнта. Якщо ви хочете повністю контролювати цей шлях, self-hosted поштовий сервер Mailcow надасть relay під вашим контролем на окремому від ERP сервері.

Резервні копії, які справді відновлюються

Дамп бази даних сам по собі не є резервною копією ERPNext. Вкладення та приватні файли зберігаються в каталозі sites, а не в MariaDB. Якщо відновити лише базу даних, кожне завантажене замовлення на придбання повернеться як непрацююче посилання.

docker compose --project-name erpnext exec backend \
  bench --site erp.example.com backup --with-files

Ця команда записує чотири файли в sites/erp.example.com/private/backups у томі sites:

  • дамп -database.sql.gz
  • архів загальнодоступних файлів -files.tar
  • архів приватних файлів -private-files.tar
  • копію конфігурації сайту -site_config_backup.json

Четвертий файл часто видаляють. Саме він створює найбільші проблеми. У ньому зберігається encryption_key — ключ, який Frappe використовує для шифрування збережених паролів: облікових даних поштових акаунтів, ключів платіжних шлюзів і всіх секретів інтеграцій. Якщо відновити базу даних без відповідного ключа, сайт завантажиться нормально, але надсилання пошти завершиться помилкою:

frappe.exceptions.ValidationError: Encryption key is invalid! Please check site_config.json

Завжди зберігайте всі чотири файли разом.

Потім перенесіть їх із сервера. Резервна копія всередині тому не переживе втрату сервера. Крім того, bench все одно видаляє її: за замовчуванням він видаляє з цього каталогу резервні копії, старші за 24 години.

docker compose --project-name erpnext cp \
  backend:/home/frappe/frappe-bench/sites/erp.example.com/private/backups \
  ~/erpnext-backups

Запускайте цю команду з cron, а потім передавайте каталог у сховище, яким ви не адмініструєте. Зашифровані резервні копії restic у зовнішньому сховищі є правильним інструментом, оскільки він шифрує дані перед завантаженням, а restic check підтверджує, що репозиторій і далі доступний для читання. Резервна копія ERP — це копія всієї вашої бухгалтерської книги, тому її слід зберігати в зашифрованому вигляді на обладнанні, яке не є цим сервером.

Перевірте відновлення до того, як воно знадобиться

Неперевірена резервна копія — це лише припущення. Перевіряйте її на другому сайті на тому самому сервері, ніколи не на робочому сайті.

docker compose --project-name erpnext exec backend \
  bench new-site --mariadb-user-host-login-scope=% \
  --db-root-password '<your DB_PASSWORD>' \
  --admin-password '<a strong admin password>' \
  restore-test.example.com

docker compose --project-name erpnext exec backend \
  bench --site restore-test.example.com --force restore \
  sites/erp.example.com/private/backups/<stamp>-erp.example.com-database.sql.gz \
  --with-public-files sites/erp.example.com/private/backups/<stamp>-erp.example.com-files.tar \
  --with-private-files sites/erp.example.com/private/backups/<stamp>-erp.example.com-private-files.tar \
  --db-root-password '<your DB_PASSWORD>'

Скопіюйте ключ шифрування з резервної конфігурації на відновлений сайт. Інакше його інтеграції не працюватимуть:

docker compose --project-name erpnext exec backend \
  bench --site restore-test.example.com set-config encryption_key '<value from site_config_backup.json>'

Тепер перевірте відновлення так, як це зробив би бухгалтер. Відкрийте звіт Accounts Receivable і порівняйте кінцевий баланс із робочим сайтом. Відкрийте нещодавній рахунок на придбання та завантажте його вкладення. Сайт, який відображає сторінку входу, нічого не доводить.

Після завершення видаліть тестовий сайт:

docker compose --project-name erpnext exec backend \
  bench drop-site restore-test.example.com

Чому фіксація версій важливіша для ERPNext

На статичному сайті тег образу без зафіксованої версії означає неочікуваний перезапуск. В ERPNext це означає міграцію схеми. bench migrate переписує таблиці бази даних і може змінювати дані документів. Скасувати ці зміни неможливо. Відкат виконується з резервної копії, а не за допомогою docker compose down.

Тому зафіксуйте тег. ERPNEXT_VERSION=v16.32.1 була версією, зафіксованою у власному репозиторії в pwd.yml у серпні 2026 року. Не використовуйте це значення надалі без перевірки. Поточні релізи наведено на сторінці релізів frappe/erpnext, а наявні теги образів — на Docker Hub. Перед оновленням прочитайте примітки до версії, на яку переходите.

Саме оновлення починається зі створення резервної копії та переходу в режим обслуговування.

docker compose --project-name erpnext exec backend \
  bench --site erp.example.com backup --with-files

docker compose --project-name erpnext exec backend \
  bench --site erp.example.com set-maintenance-mode on

Відредагуйте ERPNEXT_VERSION у ~/gitops/erpnext.env, потім виконайте render, pull і migrate.

docker compose --project-name erpnext \
  --env-file ~/gitops/erpnext.env \
  -f compose.yaml \
  -f overrides/compose.mariadb.yaml \
  -f overrides/compose.redis.yaml \
  -f overrides/compose.https.yaml \
  config > ~/gitops/erpnext.yaml

docker compose --project-name erpnext -f ~/gitops/erpnext.yaml pull
docker compose --project-name erpnext -f ~/gitops/erpnext.yaml up -d

docker compose --project-name erpnext exec backend \
  bench --site erp.example.com migrate

docker compose --project-name erpnext exec backend \
  bench --site erp.example.com set-maintenance-mode off

Режим обслуговування важливий, оскільки migrate змінює схему під час роботи. Якщо користувач надішле документ до таблиці, міграцію якої ще не завершено, записи доведеться виправляти вручну.

Переходьте між основними версіями по черзі та створюйте резервну копію між кожним кроком. Код міграції в релізі призначений для оновлення з попереднього релізу. Якщо пропустити основні версії, міграції виконуватимуться в комбінації, яку ніхто не тестував.

Репозиторій також містить overrides/compose.migrator.yaml, який додає контейнер і запускає bench --site all migrate під час кожного запуску. Це зручно. Але це також означає, що docker compose up зі зміненим тегом виконає міграцію робочої бази даних без контролю оператора. У бізнес-системі запускайте migrate лише як свідомо прийняте того ранку рішення.

Захист сервера, на якому зберігаються дані клієнтів

Змініть пароль Administrator під час першого входу. У evaluation compose file як цей пароль використовується admin, і така звичка може перейти в production.

Змініть DB_PASSWORD, щоб він відрізнявся від 123 у example.env. Це значення потрапляє у згенерований ~/gitops/erpnext.yaml у відкритому тексті, тому chmod 600 цей файл і не зберігайте його в жодному git repository. Для посилення захисту overrides/compose.mariadb-secrets.yaml зчитує пароль із Docker secret file, а не зі змінної середовища. У розділі робота з env-файлами та секретами в Docker Compose описано компроміси цього підходу.

Публікуйте лише потрібні порти. З HTTPS override назовні доступні лише порти 80 і 443. Не додавайте зіставлення ports до сервісу db, щоб спростити підключення database client: так MariaDB стане доступною з публічного інтернету. Замість цього використовуйте docker compose --project-name erpnext exec backend bench mariadb. На хості дозвольте порти 22, 80 і 443, решту забороніть. Також перевірте окремий network firewall провайдера.

Увімкніть two factor authentication у System Settings для кожного облікового запису з роллю System Manager. Ця роль дає змогу читати всі документи та експортувати всі таблиці, тому ставтеся до неї як до облікового запису адміністратора, а не як до зручної функції. Якщо ви запускаєте кілька self-hosted застосунків, Authentik як self-hosted провайдер єдиного входу буде кращим варіантом, ніж додатковий пароль для кожного застосунку.

Оновлюйте host і перезавантажуйте його після оновлень ядра. Перш ніж покладатися на автоматичний запуск stack після перезавантаження, перевірте згенерований файл на наявність політики restart для кожного сервісу, оскільки stack без такої політики не запуститься після цього перезавантаження. У розділі як налаштувати повторний запуск Docker Compose stack після перезавантаження описано налаштування systemd.

Коли ERPNext перестає комфортно працювати на одному VPS

Один VPS може тривалий час забезпечувати роботу невеликої компанії. Ознаки того, що його ресурсів уже недостатньо:

  • Фонові завдання накопичуються, тому електронні листи та імпорт завершуються із затримкою в кілька хвилин або годин.
  • docker inspect повідомляє про контейнери зі станом "OOMKilled": true або кодом завершення 137.
  • Звіти, які раніше формувалися за дві секунди, тепер формуються тридцять, а MariaDB споживає найбільше CPU.
  • Резервне копіювання триває так довго, що один запуск починається до завершення наступного запланованого запуску.

Спочатку виділіть MariaDB ресурси, які вона не ділитиме з іншими процесами. База даних і Python workers конкурують за ту саму пам’ять, а buffer pool потребує додаткової пам’яті. Збільшення ресурсів application server зазвичай дає менший ефект, ніж очікують. У матеріалі запуск бази даних у Docker або на хості розглянуто цей вибір, а налаштування memory limits у Docker Compose допомагає не допустити, щоб один контейнер вичерпав ресурси інших.

Після цього додайте queue workers, а не збільшуйте ресурси для web. Повільні операції ERPNext виконуються у фоні: формування звітів і масовий імпорт. Додаткові worker-контейнери коштують дешевше, ніж потужніший сервер, і усувають саме проблему, на яку скаржаться користувачі.

FAQ

Скільки RAM потрібно ERPNext на VPS?

Опубліковані рекомендації починаються з 4 GB і 2 vCPU, але цей рівень призначений лише для оцінювання. Для компанії, яка щодня використовує систему, плануйте 8 GB, 4 vCPU і 100 GB SSD. Якщо ресурсів менше, kernel out of memory killer зупиняє контейнери під навантаженням. docker inspect фіксує це як "OOMKilled": true з кодом завершення 137. Це початкові орієнтири, а не результати вимірювань. Тому протягом першого місяця відстежуйте фактичне використання пам’яті.

Чи можна запускати pwd.yml у production?

Ні. У README проєкту зазначено, що цей файл призначений лише для короткочасного оцінювання. Також зазначено, що встановлювати в нього custom apps не можна. Використовуйте compose.yaml з перевизначеннями для MariaDB, Redis і HTTPS. Згенеруйте з них один файл за допомогою docker compose config і запустіть цей файл.

Чому мій сайт ERPNext недоступний одразу після створення?

За замовчуванням frontend вибирає сайт для обслуговування за значенням HTTP-заголовка Host. Тому назва сайту має збігатися з доменом у браузері. Сайт, створений як erpnext, не обслуговується за адресою erp.example.com. Створіть сайт із доменом як його назвою або задайте FRAPPE_SITE_NAME_HEADER у env-файлі, вказавши назву сайту. Потім знову згенеруйте compose-файл і перезапустіть stack.

Що має входити до резервної копії ERPNext?

Потрібні 4 файли, які слід зберігати разом: дамп -database.sql.gz, архіви -files.tar і -private-files.tar та копія конфігурації -site_config_backup.json. Команда bench --site erp.example.com backup --with-files створює всі 4 файли. Копія конфігурації містить encryption_key. Тому після відновлення без неї збережені паролі інтеграцій неможливо розшифрувати. Це проявляється як Encryption key is invalid! Please check site_config.json.

Як оновити ERPNext і не пошкодити дані?

Створіть резервну копію за допомогою --with-files, увімкніть maintenance mode, змініть ERPNEXT_VERSION в env-файлі, знову згенеруйте compose-файл, виконайте pull і запустіть stack. Потім виконайте bench --site erp.example.com migrate та вимкніть maintenance mode. Переходьте лише на одну major version за раз і спочатку прочитайте release notes, оскільки migrate змінює schema та document data без можливості скасування. Для rollback відновіть резервну копію, створену на початку.