DockerでVPSにERPNextをセルフホストする方法
ERPNextをVPSでDocker運用する手順です。11コンテナ構成の必要スペック、TLS、送信メール、バージョン固定、実行済みリストアで確認するバックアップまで解説します。
運用するものの概要
VPS 上で ERPNext をセルフホストする作業は、1 つのコマンドで完了するインストールではなく、運用業務です。公式の Docker Compose スタックは 11 個のコンテナで構成され、総勘定元帳と顧客情報を保持します。そのため、以下のすべてで高い運用基準が求められます。バックアップは、リストアを実行するまでバックアップとはいえません。また、イメージタグを固定しない場合、いつでもスキーマ移行が発生する可能性があります。
ここでは、いくつかの名称が繰り返し登場します。ERPNext は業務アプリケーションです。Frappe はその基盤となる Python フレームワークです。Bench はサイトを管理するコマンドラインツールで、すでにコンテナ内にインストールされています。サイトは 1 つのテナントを指し、1 つの MariaDB データベースと、アップロードファイルを格納する 1 つのディレクトリで構成されます。ここで示すほぼすべてのコマンドは、backend コンテナ内で、指定した 1 つのサイトを対象に bench 実行します。
このガイドでは、プロジェクトが保守しているデプロイメントである frappe_docker リポジトリを使用します。以下のコマンドはすべて、2026 年 8 月にこのリポジトリを対象として確認済みです。Docker Compose 自体に慣れていない場合は、このガイドが前提とする内容を VPS 上で Docker Compose を実行する で説明しています。
ERPNext に必要な VPS の規模
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
}
]公開されている指針では、1 人のユーザーもログインしていない段階で 2 vCPU と 4 GB の RAM が必要とされています。これは評価用の構成です。これらは開始点であり、このガイドで測定した値ではありません。実際に必要な規模は、扱うドキュメント量によって決まります。最後の行は、公開された最小要件ではありません。メモリ不足を常に気にしなくて済む、おおよその水準です。
小規模なプランについては、現実的に判断してください。1 GB または 2 GB の VPS はスタックを起動できますが、最初のインポート、または時間のかかる最初のレポート処理で停止します。9 個の長時間稼働コンテナ、MariaDB のバッファープール、レポートを生成する Python ワーカーを、そのメモリ容量に収めることはできないためです。停止は正常には処理されません。カーネルの out-of-memory killer がコンテナを停止し、そのコンテナで docker inspect を実行すると、終了コード 137 とともに "OOMKilled": true が表示されます。ジョブの途中でワーカーが停止すると、送信済みのドキュメントに対するバックグラウンド処理が途中までしか完了しないことがあります。
毎日 ERPNext を使用する企業にとって、8 GB、4 vCPU、100 GB の SSD が、現実的な最低ラインです。最初に不足するのは RAM です。添付ファイルとローカルバックアップがすべてデータベースと同じボリュームに保存されるため、ディスク使用量は予想より速く増加します。
11 個のコンテナと、それぞれの役割
スタックの起動後に 9 個のコンテナが実行されている状態で、docker compose ps を実行します。残りの configurator と create-site は処理を 1 回実行して終了します。これを合わせた数が 11 個です。
backendは gunicorn で Frappe アプリケーションを実行します。benchはここで動作します。frontendは nginx です。静的アセットを配信し、それ以外のすべてのリクエストをバックエンドへ転送します。queue-shortとqueue-longは RQ(Redis Queue)のワーカーです。送信メール、インポート、レポート生成などのバックグラウンドジョブを実行します。schedulerは、スケジュール済みレポートや自動繰り返しドキュメントなど、時間ベースのジョブを起動します。websocketは、ブラウザーでのライブ更新を処理する socket.io プロセスです。dbは MariaDB です。redis-cacheとredis-queueは独立した 2 つの Redis インスタンスです。一方はキャッシュ用、もう一方はジョブキュー用です。
この分割を理解しておくことは重要です。どのログを確認すべきか判断できるためです。メールがキューで停止している場合は、キューワーカーの問題なので、docker compose logs -f queue-short が適切なコマンドです。ページは読み込まれるものの、通知バッジが更新されない場合は、WebSocket の問題です。どちらの場合も backend のログを読んでいると、調査に時間がかかるだけです。
本番用の compose ファイルでインストールする
リポジトリには pwd.yml が含まれています。README にも明記されています。「この構成は短時間の評価専用です。この構成にはカスタムアプリをインストールできません。」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 を開き、4 つの値を変更します。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 ファイルを 1 つ生成してから、起動します。
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 -dconfig は何も起動しません。ベースファイルとオーバーライドをマージし、すべての変数を置換した結果を出力します。その後、生成されたファイルを実行します。この手順には利点があります。実行中のスタックが読み取りやコミットの対象となる 1 つのファイルになるため、誰かが env ファイルを編集した場合や、リポジトリを pull した場合でも、実行中の構成が意図せず変わりません。複数の 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-appslist-apps は、バージョン付きの frappe と erpnext を出力します。正常な ps では、running 状態のサービスが 9 個表示され、restarting は 0 個です。
ここでは、2 つの問題がよく発生します。Docker では --mariadb-user-host-login-scope=% は省略できません。app コンテナは Docker ネットワーク経由で MariaDB に接続するため、MariaDB からはリモートホストとして扱われます。そのため、localhost に限定されたデータベースユーザーではログインできません。site の作成は、root ユーザーを示す MariaDB の access denied エラーで失敗します。% の範囲を指定すると、その新しい site のユーザーは、プライベートネットワーク上の任意のホストからアクセスできます。
もう 1 つは site 名です。デフォルトでは、frontend は HTTP の Host ヘッダーを基に、提供する site を選択します。そのため、erpnext として作成した site は、両方が存在していても erp.example.com では到達できません。上記のようにドメイン名を site 名にするか、env ファイルの FRAPPE_SITE_NAME_HEADER を site 名に設定して、compose ファイルを再度生成してください。
HTTPS と、動作前に満たす必要がある条件
compose.https.yaml の override は Traefik を 443 番ポートで実行し、80 番ポートへのアクセスを Traefik にリダイレクトして、Let's Encrypt に証明書を要求します。TLS(transport layer security)は、請求書やセッション Cookie が平文のままネットワーク上を流れないようにします。
証明書を発行するには、2 つの条件を満たす必要があります。満たしていない場合、証明書は発行されません。erp.example.com の DNS A レコードが、あらかじめ VPS を指している必要があります。Let's Encrypt は 80 番ポートで HTTP-01 challenge を実行し、名前を管理していることを確認するため、80 番ポートと 443 番ポートがインターネットから到達可能でなければなりません。プロバイダー側のネットワーク firewall と、サーバー上の firewall の両方を確認してください。これらは別々の制御であり、panel firewall は見落とされがちです。
証明書は cert-data volume の /letsencrypt/acme.json に保存されます。ブラウザーに自分の証明書ではなく default certificate が表示される場合は、docker compose --project-name erpnext ps で proxy service の名前を確認し、そのログから ACME(automatic certificate management environment)のエラーを調べてください。同じサーバーでほかの Web アプリケーションも実行していますか。複数の Docker Compose アプリで 1 つの Traefik instance を前段で共有する方法では、443 番ポートを奪い合う代わりに proxy を共有する方法を説明しています。
送信メール。請求書がサーバーから出ていかない場合
これは多くの ERPNext ガイドが省略している手順ですが、システムを実用にできるかどうかを左右します。送信メールが機能しなければ、請求書は顧客に届かず、パスワードリセットのメールも届かず、スケジュール済みレポートも配信されません。この構成にはメールサーバーが含まれていません。
VPS から port 25 を使って直接メールを送信しないでください。多くのプロバイダーは、新規アカウントでの送信 port 25 をブロックしています。送信できたメールも、新しい VPS のアドレスには送信元評価がないため、拒否されるか、スパムとして処理されます。port 587 の認証済みリレーを使用してください。
推奨される方法は、ERPNext インターフェースの Email Account 画面を使用することです。パスワードは暗号化して保存されます。キーを site config に書き込むこともできます。
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" ではなく数値として保存します。ファイルを読み返し、この2つの値が引用符で囲まれていないことを確認してください。
docker compose --project-name erpnext exec backend \
cat sites/erp.example.com/site_config.jsonmail_password はコマンドラインではなく Email Account 画面で設定してください。暗号化して保存でき、シェル履歴にも残りません。
次に、実際にメールを送信します。Sales Invoice を作成し、管理しているアドレスにメールで送信します。その間、キューを監視してください。
docker compose --project-name erpnext logs -f queue-short送信メールはバックグラウンドジョブとして処理されます。そのため、メールが届かない場合は、ブラウザーのエラーではなく、そのログに failed job として表示されることが一般的です。送信ドメインには SPF(sender policy framework)と DKIM(domainkeys identified mail)のレコードも公開し、DMARC ポリシーを追加してください。これらがないと、技術的には正しい請求書でも顧客のスパムフォルダーに振り分けられます。経路全体を自分で管理したい場合は、自己ホスト型の Mailcow メールサーバーを使用できます。ERPNext とは別のサーバーで、自分が管理するリレーを構築できます。
実際にリストアできるバックアップ
データベースダンプだけでは、ERPNext のバックアップになりません。添付ファイルとプライベートファイルは MariaDB ではなく sites ディレクトリに保存されます。データベースだけをリストアすると、アップロード済みの発注書はすべてリンク切れになります。
docker compose --project-name erpnext exec backend \
bench --site erp.example.com backup --with-filesこれにより、sites ボリューム内の sites/erp.example.com/private/backups に次の4つのファイルが作成されます。
-database.sql.gzダンプ- 公開ファイルの
-files.tarアーカイブ - プライベートファイルの
-private-files.tarアーカイブ - サイト設定の
-site_config_backup.jsonコピー
4つ目のファイルは削除されがちですが、最も重要なファイルです。このファイルには encryption_key が含まれています。これは、Frappe が保存済みパスワードの暗号化に使用するキーです。メールアカウントの認証情報、決済ゲートウェイのキー、すべての連携用 Secret が対象です。一致するキーなしでデータベースをリストアすると、サイトは正常に読み込まれますが、メール送信は次のエラーで失敗します。
frappe.exceptions.ValidationError: Encryption key is invalid! Please check site_config.json必ず4つのファイルを一緒に保管してください。
次に、サーバーの外部へコピーします。ボリューム内のバックアップはサーバー障害に耐えられません。また、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>'次に、会計担当者が確認するようにリストアを検証します。売掛金レポートを開き、期末残高を本番サイトと比較してください。最近の仕入請求書を開き、添付ファイルをダウンロードしてください。ログインページが表示されるだけでは、何も証明できません。
完了したらテストサイトを削除します。
docker compose --project-name erpnext exec backend \
bench drop-site restore-test.example.comERPNext でバージョン固定がより重要な理由
静的サイトでは、イメージタグを固定しないと予期しない再起動が発生します。ERPNext では、データベーススキーマのマイグレーションが発生します。bench migrate はデータベーステーブルを書き換え、ドキュメントデータを書き換えることもあります。元に戻す機能はありません。ロールバックはバックアップからのリストアであり、docker compose down ではありません。
そのため、タグを固定してください。ERPNEXT_VERSION=v16.32.1 は、2026 年 8 月にリポジトリ独自の pwd.yml で固定されていたリリースです。この番号を確認せずに使い続けないでください。現在のリリースは 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~/gitops/erpnext.env の ERPNEXT_VERSION を編集し、その後に 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 の実行中にスキーマが変更されるためです。マイグレーション途中のテーブルに対してユーザーがドキュメントを送信すると、レコードを手作業で修復することになります。
メジャーバージョンは一度に 1 つだけ進め、各ステップの間にバックアップを取得してください。リリースのマイグレーションコードは、直前のリリースからのアップグレードを前提に作られています。そのため、メジャーバージョンを飛ばすと、誰もテストしていない組み合わせでマイグレーションが実行されます。
リポジトリには overrides/compose.migrator.yaml も含まれています。これは、起動のたびに bench --site all migrate を実行するコンテナを追加します。便利な機能です。一方で、タグを変更した docker compose up によって、誰も監視していない状態で本番データベースがマイグレーションされることも意味します。業務システムでは、migrate はその日の判断として実行してください。
顧客記録を保持するサーバーのセキュリティ強化
初回ログイン時に Administrator のパスワードを変更します。評価用の compose ファイルでは、admin がそのパスワードとして設定されています。この習慣を本番環境に持ち込まないでください。
example.env にある 123 から DB_PASSWORD を変更します。その値は、レンダリングされた ~/gitops/erpnext.yaml に平文で残ります。そのため、chmod 600 ファイルを管理し、git リポジトリには置かないでください。より強固な方法として、overrides/compose.mariadb-secrets.yaml は環境変数ではなく Docker secret ファイルからパスワードを読み込みます。Docker Compose での環境変数ファイルと Secret の扱いでは、これらのトレードオフを説明しています。
必要なポートだけを公開します。HTTPS 用の override では、公開されるポートは 80 と 443 だけです。データベースクライアントから接続しやすくするために、db サービスへ ports のマッピングを追加しないでください。MariaDB がパブリックインターネットに公開されます。代わりに docker compose --project-name erpnext exec backend bench mariadb を使用します。ホストでは 22、80、443 を許可し、それ以外を拒否します。プロバイダー側に別のネットワークファイアウォールがある場合は、それも確認してください。
System Settings で、System Manager ロールを持つすべてのアカウントに 2 要素認証を有効にします。このロールではすべてのドキュメントを読み取り、すべてのテーブルをエクスポートできます。そのため、単なる利便性のためのアカウントではなく、管理者アカウントとして扱ってください。複数のセルフホストアプリを運用する場合は、アプリごとにパスワードを増やすよりも、セルフホスト型のシングルサインオンプロバイダーとしての Authentikを使用する方が適しています。
ホストにパッチを適用し、カーネル更新後に再起動します。再起動後にスタックが復旧すると考える前に、レンダリングされたファイルで各サービスに restart ポリシーが設定されているか確認してください。設定されていないスタックは、その再起動後に停止したままになります。Docker Compose スタックを再起動後に再び起動させる方法では、systemd 側の設定を説明しています。
1 台の VPS では ERPNext の運用が難しくなるとき
1 台の VPS で小規模な会社の業務を長期間支えられる場合があります。次の兆候が現れたら、容量が不足しています。
- バックグラウンドジョブが滞留し、メールやインポートの反映が数分から数時間遅れます。
docker inspectが"OOMKilled": trueまたは終了コード 137 を報告します。- 2 秒で完了していたレポートに 30 秒かかるようになり、CPU を使用しているプロセスが MariaDB になります。
- バックアップの実行時間が長くなり、次のスケジュール実行と重なります。
まず、MariaDB に専用のリソースを割り当てます。データベースと Python ワーカーが同じメモリを奪い合い、より多くのメモリを必要とするのはバッファプールだからです。アプリケーションサーバーを大きくしても、期待するほど改善しません。データベースを Docker またはホスト上で実行する ではこの構成を選択する方法を説明し、Docker Compose でメモリ制限を設定する では、その間に 1 つのコンテナが他のコンテナのリソースを枯渇させないようにします。
次に、Web の容量ではなくキューワーカーを追加します。ERPNext で時間のかかる処理は、レポート生成や一括インポートなどのバックグラウンド処理です。ワーカーコンテナを増やす方が大きなサーバーを用意するより低コストで、ユーザーが実際に困っている問題を解消できます。
FAQ
VPS で ERPNext を実行するには、どの程度の RAM が必要ですか?
公開されているガイダンスでは、4 GB の RAM と 2 vCPU が最低ラインとされています。ただし、この構成は評価用に限られます。日常的に利用する企業では、8 GB の RAM、4 vCPU、100 GB の SSD を計画してください。それを下回ると、負荷時に kernel の out of memory killer がコンテナを停止します。この状態は docker inspect では終了コード 137 の "OOMKilled": true として報告されます。これらは測定値ではなく開始点です。そのため、最初の 1 か月は実際のメモリ使用量を監視してください。
本番環境で pwd.yml を実行できますか?
いいえ。プロジェクトの README では、短期間の評価専用と説明されています。また、カスタムアプリをインストールできないことも記載されています。MariaDB、Redis、HTTPS のオーバーライドを含む compose.yaml を使用し、docker compose config でそれらを単一ファイルにレンダリングして、そのファイルを実行してください。
作成直後に ERPNext サイトへ接続できないのはなぜですか?
デフォルトでは、フロントエンドは HTTP の Host ヘッダーから配信するサイトを選択します。そのため、サイト名はブラウザーで使用するドメインと一致している必要があります。erpnext として作成したサイトは、erp.example.com では配信されません。ドメインをサイト名としてサイトを作成するか、env ファイルの FRAPPE_SITE_NAME_HEADER にサイト名を設定してください。その後、compose ファイルを再度レンダリングして、スタックを再起動します。
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 でバックアップを作成し、メンテナンスモードを有効にします。env ファイルの ERPNEXT_VERSION を変更し、compose ファイルを再度レンダリングしてから pull とスタックの起動を行います。次に bench --site erp.example.com migrate を実行し、メンテナンスモードを無効にします。メジャーバージョンは一度に 1 つだけ進め、最初にリリースノートを確認してください。migrate はスキーマとドキュメントデータを書き換えるため、元に戻せません。ロールバックする場合は、開始時に作成したバックアップを復元します。