Fixing ERPNext / Frappe Error
pymysql.err.OperationalError: (1045) Access denied for user
Environment
- OS: RHEL (private / offline)
- Deployment: Docker Compose
- Stack: Frappe + ERPNext + MariaDB
- Setup: Multi-site (
s1,s2,s3) with separate MariaDB containers - Access: SSH only (production)
🔴 The Problem
While installing ERPNext on a new site:
bench --site s3.inxeoz.com install-app erpnext
The installation failed with:
pymysql.err.OperationalError: (1045,
"Access denied for user 's3_db'@'172.21.0.9' (using password: YES)")
ERPNext could not connect to the database.
🟡 Impact Analysis
- ❌ ERPNext installation blocked
- ❌ Only one site (
s3) affected - ✅ Other sites (
s1,s2) working - ✅ No data loss
- ✅ No service outage
This is a database authentication issue, not an ERPNext bug.
🧠 Root Cause (Important)
MariaDB user s3_db existed, but it was bound to a single Docker IP:
's3_db'@'172.21.0.8'
Meanwhile, the Frappe backend container connected from:
172.21.0.9
Docker assigns IPs dynamically. MariaDB treats each IP as a different host.
➡️ Result: Access denied, even with the correct password.
🔍 Verification Steps (Read-Only, Safe)
1️⃣ Check site database configuration
Inside backend container:
cat sites/s3.inxeoz.com/site_config.json
Example output:
{
"db_host": "mariadb-s3",
"db_name": "s3_db",
"db_password": "hsWcj7II1yEoSqZC",
"db_type": "mariadb"
}
2️⃣ Identify the correct MariaDB container
On the host:
docker ps
Relevant output:
mariadb-s3 mariadb:11.8 Up
⚠️ Container name matters
mariadb-s3 ≠ mariadb_s3
🛠️ Safe Fix (No Restarts, No Data Loss)
⚠️ Preconditions
- You are on the Docker host
- You know the MariaDB root password
- You do not restart containers
3️⃣ Enter the MariaDB container
docker exec -it mariadb-s3 mariadb -u root -p
4️⃣ Inspect the existing user (read-only)
SELECT Host, User FROM mysql.user WHERE User='s3_db';
Output:
+------------+-------+
| Host | User |
+------------+-------+
| 172.21.0.8 | s3_db |
+------------+-------+
✅ Confirms the problem: user restricted to one IP.
5️⃣ Create a wildcard user (correct approach)
CREATE USER 's3_db'@'%' IDENTIFIED BY 'hsWcj7II1yEoSqZC';
Why %?
- Docker IPs change
- Containers must connect from any host
6️⃣ Grant database privileges
GRANT ALL PRIVILEGES ON s3_db.* TO 's3_db'@'%';
FLUSH PRIVILEGES;
7️⃣ (Optional) Remove the IP-locked user
This prevents future confusion.
DROP USER 's3_db'@'172.21.0.8';
FLUSH PRIVILEGES;
⚠️ Safe only because % user already exists
✅ Mandatory Verification (Do NOT Skip)
Exit MariaDB:
EXIT;
Test login exactly like Frappe does:
docker exec -it mariadb-s3 mariadb -u s3_db -p s3_db
Expected result:
MariaDB [s3_db]>
If this fails → stop and fix DB first.
🚀 Final Step: Install ERPNext
Enter backend container:
docker compose -p frappe exec backend bash
Run:
bench --site s3.inxeoz.com install-app erpnext
This should now complete successfully.
✅ Post-Install Verification
bench --site s3.inxeoz.com list-apps
Expected:
frappe
erpnext
❌ What NOT To Do (Production Rules)
- ❌ Do NOT bind DB users to Docker IPs
- ❌ Do NOT use
'user'@'localhost' - ❌ Do NOT delete databases
- ❌ Do NOT recreate the site
- ❌ Do NOT restart MariaDB or Docker
- ❌ Do NOT guess passwords
🧠 Key Lessons (Critical)
✅ Correct pattern for Docker + Frappe
'user'@'%'
❌ Incorrect patterns
'user'@'172.21.x.x'
'user'@'localhost'
Docker networking is not stable. MariaDB authentication must be flexible.
📌 Quick Checklist for Future Sites
Before installing apps:
-
site_config.jsonexists - MariaDB container running
- DB user exists as
'user'@'%' - Manual DB login test passes
- No container restarts done
If all are true → ERPNext will install.