I self-hosted ERPNext — bench new-site could not reach MariaDB

I self-hosted ERPNext — bench new-site could not reach MariaDB

I wanted one system for invoices and stock instead of a pile of spreadsheets. ERPNext on Ubuntu 24.04 with frappe_docker came up, then bench new-site could not connect to MariaDB until DB_PASSWORD in erpnext.env matched the secret I generated.

· Updated · 4 min read #self-hosted #open-source #erpnext #erp #business #python #frappe #accounting #deployment #docker #vps

 ERPNext self-hosted business management suite

Caption: Accounting, stock, and HR in one browser app. MariaDB and Redis in Compose. HTTPS via nginx-proxy. This is a business database, not a toy container.

Why I wanted this on my server

Invoices, stock, and customer balances do not belong in five SaaS logins and a spreadsheet named final-final.xlsx. ERPNext on Frappe is the open-source suite I was willing to host: selling, buying, inventory, a chart of accounts.

Self-hosting is the point. I decide where those records live and whether I can restore them. It is not set-and-forget. Permissions, fiscal year, and backups matter more than the first login.

What I actually installed

Official frappe/frappe_docker under /opt/frappe_docker, generated Compose in ~/gitops. MariaDB, Redis, backend workers, scheduler, websocket, frontend Nginx, nginx-proxy + acme-companion. Site erp.example.com. Floor: 2 vCPU / 4 GB; 4 / 8 is more honest once reports and jobs run. Docker Engine 23+.

pwd.yml in that repo is a demo. I did not use it for a hostname I care about.

Where it broke

On a fresh Ubuntu 24.04 box this install is famous for bench new-site failing on MariaDB.

Containers looked up. Then:

docker compose --project-name erpnext -f ~/gitops/erpnext.yaml exec backend \
  bench new-site ...

could not connect. I had generated ERPNEXT_DB_ROOT_PASSWORD, written erpnext-secrets.env, and only half-updated DB_PASSWORD in ~/gitops/erpnext.env. The db service and bench were not using the same password.

Fix: DB_PASSWORD in erpnext.env matches the value in erpnext-secrets.env, db is actually running, then new-site again with --db-root-password from that file.

HTTPS failing while containers are green is the other common one: DNS, ports 80/443, NGINX_PROXY_HOSTS exact hostname, then acme-companion logs.

 ERPNext Docker Compose stack

Caption: Frontend, workers, scheduler, websockets, two Redis, MariaDB, proxy. Site name has to match the hostname people type.

Prerequisites

Domain pointing at the box before Let’s Encrypt. SMTP later for invoices. Named users — not shared Administrator for daily work. Backup DB and site files.

sudo apt update
sudo apt upgrade -y
sudo apt install -y ca-certificates curl git gnupg openssl ufw jq

sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status

The working install

1. Install Docker Engine

curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker "$USER"
newgrp docker

docker --version
docker compose version

2. Clone Frappe Docker

sudo mkdir -p /opt/frappe_docker
sudo chown -R "$USER":"$USER" /opt/frappe_docker

git clone https://github.com/frappe/frappe_docker.git /opt/frappe_docker
mkdir -p ~/gitops
chmod 700 ~/gitops

cd /opt/frappe_docker

3. Create the environment file

Replace erp.example.com and admin@example.com first:

cd /opt/frappe_docker
cp example.env ~/gitops/erpnext.env
chmod 600 ~/gitops/erpnext.env

umask 077
ERPNEXT_DB_ROOT_PASSWORD="$(openssl rand -base64 48 | tr -dc 'A-Za-z0-9' | head -c 32)"
ERPNEXT_ADMIN_PASSWORD="$(openssl rand -base64 48 | tr -dc 'A-Za-z0-9' | head -c 32)"

python3 - <<'PY'
from pathlib import Path
import os

env_path = Path.home() / "gitops" / "erpnext.env"
text = env_path.read_text()
text = text.replace("DB_PASSWORD=123", f"DB_PASSWORD={os.environ['ERPNEXT_DB_ROOT_PASSWORD']}")
text += "\nNGINX_PROXY_HOSTS=erp.example.com\n"
text += "LETSENCRYPT_EMAIL=admin@example.com\n"
env_path.write_text(text)
PY

cat > ~/gitops/erpnext-secrets.env <<EOF
ERPNEXT_DB_ROOT_PASSWORD=${ERPNEXT_DB_ROOT_PASSWORD}
ERPNEXT_ADMIN_PASSWORD=${ERPNEXT_ADMIN_PASSWORD}
EOF

jq -n --arg site "erp.example.com" '{"site": $site, "note": "Store ~/gitops/erpnext-secrets.env in a password manager or encrypted vault."}'

Those two env files stay off public git.

4. Render the Compose file

cd /opt/frappe_docker

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.nginxproxy.yaml \
  -f overrides/compose.nginxproxy-ssl.yaml \
  config > ~/gitops/erpnext.yaml

chmod 600 ~/gitops/erpnext.yaml
docker compose --project-name erpnext -f ~/gitops/erpnext.yaml config --services

A rendered file is something I can diff before an upgrade.

5. Start the stack

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 -f ~/gitops/erpnext.yaml ps
docker compose --project-name erpnext -f ~/gitops/erpnext.yaml logs --since=10m backend frontend db redis-cache redis-queue
docker compose --project-name erpnext -f ~/gitops/erpnext.yaml logs --since=10m nginx-proxy acme-companion

6. Create the ERPNext site

set -a
. ~/gitops/erpnext-secrets.env
set +a

docker compose --project-name erpnext -f ~/gitops/erpnext.yaml exec backend \
  bench new-site --mariadb-user-host-login-scope=% \
  --db-root-password "${ERPNEXT_DB_ROOT_PASSWORD}" \
  --install-app erpnext \
  --admin-password "${ERPNEXT_ADMIN_PASSWORD}" \
  erp.example.com

Site name = hostname. Another hostname later means proxy list, re-render erpnext.yaml, restart, then create or map that site on purpose.

7. Verify HTTPS and first login

curl -I https://erp.example.com
docker compose --project-name erpnext -f ~/gitops/erpnext.yaml exec backend \
  bench --site erp.example.com list-apps

Sign in as Administrator with ERPNEXT_ADMIN_PASSWORD. Then named admin accounts. I do not live as Administrator.

Very Important to remember

Background work is Redis + scheduler. Login page up but “submit” hangs: bench --site erp.example.com scheduler status and the queue Redis services. Large imports timing out: workers, RAM, proxy timeouts — not a missing checkbox in the wizard.

Wizard still matters: country, timezone, currency, company, fiscal year, chart of accounts. Wrong opening balances are not a Docker problem. I stop posting real invoices until finance looks at GL and taxes.

Email: SMTP inside ERPNext, then a test mail. Uploads failing: volume disk and proxy body size.

First workflow I actually ran

docker compose --project-name erpnext -f ~/gitops/erpnext.yaml exec backend \
  bench --site erp.example.com doctor

docker compose --project-name erpnext -f ~/gitops/erpnext.yaml exec backend \
  bench --site erp.example.com scheduler status

 ERPNext business workflow

Caption: Customer → quotation → order → invoice → payment, plus stock. If that path works, the stack is more than a login screen.

Test customer, supplier, one item. Quotation → sales order → sales invoice → payment. Purchase receipt or stock entry. Then GL, stock ledger, AR. Cancel or delete the junk per policy before real books.

Backup, expose, next step

docker compose --project-name erpnext -f ~/gitops/erpnext.yaml exec backend \
  bench --site erp.example.com backup --with-files

--with-files is public + private. The files still sit in a Docker volume until I copy them off-box (restic, borg, rclone, whatever I already trust). Restore on a separate environment before I believe it.

sudo crontab -e
0 */6 * * * docker compose --project-name erpnext -f /home/ubuntu/gitops/erpnext.yaml exec backend bench --site all backup --with-files >/var/log/erpnext-backup.log 2>&1

Adjust the path if the gitops dir is not /home/ubuntu/gitops.

 ERPNext backup and restore plan

Caption: DB dump, public files, private files, encrypted copy elsewhere, restore rehearsal. Upgrade notes for accounting versions.

What I have running now is ERPNext on erp.example.com over HTTPS, one site, MariaDB password consistent, wizard not finished with real opening balances. Next I lock roles, connect SMTP, and restore a --with-files backup onto a staging compose project.

Did you hit the same wall?

I got stuck on bench new-site not connecting to MariaDB because DB_PASSWORD in erpnext.env did not match the secret I passed to --db-root-password. Did you hit the same thing, or a different one? Tell me in the comments. I read them.

Need this done on your server?

I deploy and harden Laravel/CodeCanyon apps on cPanel or VPS, and offer monthly Server Watch retainers. Hire for deploy · Care plan

References

Share:

Get new posts in your inbox

No spam. One short email per new article — practical PHP, Laravel, devops, and AI-assisted workflows.

Comments

Powered by GitHub Discussions via Giscus. A free GitHub account is required.