Installing ERPNext v15 on Debian 13 Using Docker (Production Guide)
This guide explains how to install ERPNext v15 on a single Debian 13 server using Docker and Docker Compose, following Frappe’s recommended production approach.
The setup is suitable for:
- Single ERPNext instance
- Production workloads
- Existing or new web servers (Apache / Nginx / Traefik / Caddy)
Architecture Overview (Important to understand first)
This setup runs all ERPNext services inside Docker containers, fully isolated from the host OS.
| Component | Purpose |
|---|---|
| frappe/erpnext | ERPNext + Frappe framework |
| MariaDB 10.6 | ERPNext database (containerized) |
| Redis (cache + queue) | Background jobs, caching |
| Docker volumes | Persistent data storage |
| Reverse proxy | SSL & domain handling |
Why Docker MariaDB instead of MariaDB installed on the host?
- No dependency conflicts
- Correct, tested database version
- No interference with host MySQL/MariaDB on port 3306
- Persistent data via Docker volumes
ERPNext Docker Architecture (Interactive)
HTTPS :443
SSL Termination
(Nginx)
(Frappe / Bench)
Step 0 – Install Docker & Docker Compose (run as root)
apt update apt install -y ca-certificates curl gnupg
install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg chmod a+r /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \ https://download.docker.com/linux/debian $(. /etc/os-release && echo "$VERSION_CODENAME") stable" \ > /etc/apt/sources.list.d/docker.list
apt update apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
Step 1 – Add user to docker group (run as root)
adduser erp usermod -aG docker erp
• Adding to docker group means you no longer need sudo to run docker commands
• Permission Alignment: All the files created will be under the user ownership
Step 2 – Install Git (run as root)
apt install -y git
Switch to non-root user
su - erp
Step 3 – Clone Frappe Docker repository (non-root)
git clone https://github.com/frappe/frappe_docker cd frappe_docker
Step 4 – Create .env file
ERPNEXT_VERSION=v15.95.2 DB_PASSWORD=STRONG_DB_PASSWORD FRAPPE_SITE_NAME_HEADER=example.com HTTP_PUBLISH_PORT=8080 UPSTREAM_REAL_IP_ADDRESS=127.0.0.1 UPSTREAM_REAL_IP_HEADER=X-Forwarded-For
Note: Use a strong password for MySQL root user.
Step 5 – Fix MariaDB version (important)
mariadb:11.8.ERPNext v15 is tested with MariaDB ≤ 10.8.
You may see this warning:
Warning: MariaDB version ['11.8', '5'] is more than 10.8 which is not yet tested with Frappe Framework.
Edit:
overrides/compose.mariadb.yaml
Change:
image: mariadb:11.8
To:
image: mariadb:10.6
Step 6 – Start ERPNext stack
docker compose \ -f compose.yaml \ -f overrides/compose.mariadb.yaml \ -f overrides/compose.redis.yaml \ -f overrides/compose.noproxy.yaml \ up -d
Step 7 – Verify containers
docker compose ls
You should see:
- frontend
- backend
- websocket
- queue-short / queue-long
- scheduler
- redis-cache / redis-queue
- mariadb (healthy)
Step 8 – Create ERPNext site
docker compose exec backend bench new-site example.com \ --mariadb-root-password DB_PASSWORD \ --admin-password ADMIN_PASSWORD \ --install-app erpnext
Important:
• --mariadb-root-password must match DB_PASSWORD in .env
• --admin-password is used to log in — note it down
Step 9 – Enable scheduler
docker compose exec backend bench --site example.com enable-scheduler docker compose exec backend bench --site example.com scheduler enable docker compose exec backend bench doctor
bench doctor is your "General Health Check" tool
Expected output:
-----Checking scheduler status----- Workers online: 2 -----internal.shenzhen24h.com Jobs-----
Step 10 – Access ERPNext
Open in browser:
http://SERVER-IP:8080/app
Login:
- Username:
Administrator - Password: The one you set in Step 8
Step 11 – Reverse proxy & SSL Examples
Apache
<VirtualHost *:443>
ServerName example.com
ProxyPreserveHost On
RequestHeader set X-Forwarded-For "%{REMOTE_ADDR}s"
RequestHeader set X-Forwarded-Proto "https"
ProxyPass / http://127.0.0.1:8080/
ProxyPassReverse / http://127.0.0.1:8080/
</VirtualHost>a2enmod proxy proxy_http headers rewrite systemctl restart apache2
Nginx
server {
listen 443 ssl;
server_name example.com;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto https;
}
}Step 12 – If no web server exists
Option A – Traefik
- Automatic SSL
- Docker-native
- Uses
compose.https.yaml
Option B – Caddy
- Very simple configuration
- Automatic HTTPS
- Ideal for single-server deployments
Step 13 – Data persistence & backups
ERPNext data is stored in Docker volumes, not inside containers.
Containers can be safely recreated or upgraded without data loss.
Persistent Docker volumes
- Database:
frappe_docker_db-data - Sites & file uploads:
frappe_docker_sites - Redis queue data:
frappe_docker_redis-queue-data
Important: Containers are disposable; volumes are persistent.
Recommended backup method (ERPNext-aware)
The preferred way to back up ERPNext is using bench backup inside the backend container.
docker compose exec backend bench \ --site internal.shenzhen24h.com backup --with-files
This creates a complete backup including:
- Site configuration
- MariaDB database (compressed SQL)
- Public files
- Private files
Example output:
Backup Summary for internal.shenzhen24h.com Config : site_config_backup.json Database: database.sql.gz Public : files.tar Private : private-files.tar Backup completed successfully
Backups are stored inside the sites volume:
sites/internal.shenzhen24h.com/private/backups/
Optional: Volume-level backups (infrastructure safety)
For disaster recovery, you may also back up Docker volumes at the filesystem level:
- Snapshot
frappe_docker_db-data - Archive
frappe_docker_sites
This is useful for full server restores, but bench backups should still be taken regularly.
Common Issues & Fixes
| Issue | Fix |
|---|---|
| MariaDB 11.x warning | Downgrade to 10.6 |
| Scheduler disabled | Enable manually |
| Login loop | Missing proxy headers |
| RequestHeader error | Enable mod_headers |
| Port 8080 exposed | Use reverse proxy only |
Best Practices
- Run Docker as non-root user
- Use strong DB passwords
- Do not expose ERPNext directly to the internet
- Use reverse proxy + SSL
- Backup Docker volumes regularly
Conclusion
This setup follows Frappe’s official Docker recommendations and provides a stable, secure, production-ready ERPNext installation on Debian 13.











