ERPNext v15 Docker Setup Guide
FORCETOC
ERPNext v15 — Docker Setup Guide
Based on the original tutorial by Rein Simacon. Updated 2025 | ERPNext v15.31.3 | Docker Compose v2
Purpose
This guide walks you through running ERPNext v15 on your local machine using Docker. A Docker-based setup starts faster and uses fewer resources than a virtual machine — making it ideal for:
- Students exploring ERPNext, database design, REST APIs, and webhooks in a live environment
- IT staff and evaluators testing ERPNext before committing to a full deployment
- Thin-client demos — ERPNext v15 can comfortably serve companies under 50 users on modest hardware
If you break something, you can tear it all down and start fresh in minutes.
Default Credentials
| Field | Value |
|---|---|
| URL | http://localhost:8080
|
| Username | Administrator
|
| Password | admin
|
⚠ These are for local testing only. Never expose this setup to the internet without changing passwords and adding HTTPS.
Minimum Hardware Requirements
| Resource | Minimum | Recommended |
|---|---|---|
| RAM | 4 GB | 8 GB |
| CPU | 2 cores | 4 cores |
| Disk | 10 GB free | 20 GB free |
RAM is the critical constraint. ERPNext runs multiple services simultaneously (MariaDB, Redis ×2, backend workers, scheduler, websocket, nginx). Less than 4 GB will cause containers to crash-loop.
Architecture Overview
ERPNext in Docker is composed of several services that work together:
| Service | Role |
|---|---|
frontend |
Nginx reverse proxy — serves the web UI on port 8080 |
backend |
Frappe/ERPNext Python application server |
websocket |
Node.js Socket.IO server for real-time updates |
queue-long |
Background worker for long-running jobs (imports, reports) |
queue-short |
Background worker for short jobs (emails, notifications) |
scheduler |
Cron-like service for scheduled tasks |
db |
MariaDB 10.6 database |
redis-cache |
In-memory cache for session and page data |
redis-queue |
Message broker for the job queues |
configurator |
One-time setup container (runs once, then exits) |
create-site |
One-time site creation container (runs once, then exits) |
Step 1 — Install Docker
Install Docker Engine and the Docker Compose plugin (v2).
Windows
Download and install Docker Desktop: docker.com/products/docker-desktop
Docker Desktop bundles both Docker Engine and the Compose plugin.
Ubuntu / Debian Linux
# Install Docker Engine
curl -fsSL https://get.docker.com | sh
# Add your user to the docker group (so you don't need sudo every time)
sudo usermod -aG docker $USER
# Log out and back in, then verify
docker --version
docker compose version
Note: This guide uses docker compose (v2, no hyphen). The older docker-compose (v1) is deprecated. If your system only has v1, replace docker compose with docker-compose in all commands below.
Useful References
Step 2 — Create a Working Directory
mkdir -p ~/docker/erpnext
cd ~/docker/erpnext
Step 3 — Create the Docker Compose File
Create a file named pwd.yml:
nano pwd.yml
Paste the following content:
services:
backend:
image: frappe/erpnext:v15.31.3
deploy:
restart_policy:
condition: on-failure
volumes:
- sites:/home/frappe/frappe-bench/sites
- logs:/home/frappe/frappe-bench/logs
configurator:
image: frappe/erpnext:v15.31.3
deploy:
restart_policy:
condition: none
entrypoint:
- bash
- -c
command:
- >
ls -1 apps > sites/apps.txt;
bench set-config -g db_host $$DB_HOST;
bench set-config -gp db_port $$DB_PORT;
bench set-config -g redis_cache "redis://$$REDIS_CACHE";
bench set-config -g redis_queue "redis://$$REDIS_QUEUE";
bench set-config -g redis_socketio "redis://$$REDIS_QUEUE";
bench set-config -gp socketio_port $$SOCKETIO_PORT;
environment:
DB_HOST: db
DB_PORT: "3306"
REDIS_CACHE: redis-cache:6379
REDIS_QUEUE: redis-queue:6379
SOCKETIO_PORT: "9000"
volumes:
- sites:/home/frappe/frappe-bench/sites
- logs:/home/frappe/frappe-bench/logs
create-site:
image: frappe/erpnext:v15.31.3
deploy:
restart_policy:
condition: none
volumes:
- sites:/home/frappe/frappe-bench/sites
- logs:/home/frappe/frappe-bench/logs
entrypoint:
- bash
- -c
command:
- >
wait-for-it -t 120 db:3306;
wait-for-it -t 120 redis-cache:6379;
wait-for-it -t 120 redis-queue:6379;
export start=`date +%s`;
until [[ -n `grep -hs ^ sites/common_site_config.json | jq -r ".db_host // empty"` ]] && \
[[ -n `grep -hs ^ sites/common_site_config.json | jq -r ".redis_cache // empty"` ]] && \
[[ -n `grep -hs ^ sites/common_site_config.json | jq -r ".redis_queue // empty"` ]];
do
echo "Waiting for sites/common_site_config.json to be created";
sleep 5;
if (( `date +%s`-start > 120 )); then
echo "could not find sites/common_site_config.json with required keys";
exit 1
fi
done;
echo "sites/common_site_config.json found";
bench new-site --no-mariadb-socket --admin-password=admin --db-root-password=admin --install-app erpnext --set-default frontend;
db:
image: mariadb:10.6
healthcheck:
test: mysqladmin ping -h localhost --password=admin
interval: 1s
retries: 15
deploy:
restart_policy:
condition: on-failure
command:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
- --skip-character-set-client-handshake
- --skip-innodb-read-only-compressed
environment:
MYSQL_ROOT_PASSWORD: admin
volumes:
- db-data:/var/lib/mysql
frontend:
image: frappe/erpnext:v15.31.3
depends_on:
- websocket
deploy:
restart_policy:
condition: on-failure
command:
- nginx-entrypoint.sh
environment:
BACKEND: backend:8000
FRAPPE_SITE_NAME_HEADER: frontend
SOCKETIO: websocket:9000
UPSTREAM_REAL_IP_ADDRESS: 127.0.0.1
UPSTREAM_REAL_IP_HEADER: X-Forwarded-For
UPSTREAM_REAL_IP_RECURSIVE: "off"
PROXY_READ_TIMEOUT: 120
CLIENT_MAX_BODY_SIZE: 50m
volumes:
- sites:/home/frappe/frappe-bench/sites
- logs:/home/frappe/frappe-bench/logs
ports:
- "8080:8080"
queue-long:
image: frappe/erpnext:v15.31.3
deploy:
restart_policy:
condition: on-failure
command:
- bench
- worker
- --queue
- long,default,short
volumes:
- sites:/home/frappe/frappe-bench/sites
- logs:/home/frappe/frappe-bench/logs
queue-short:
image: frappe/erpnext:v15.31.3
deploy:
restart_policy:
condition: on-failure
command:
- bench
- worker
- --queue
- short,default
volumes:
- sites:/home/frappe/frappe-bench/sites
- logs:/home/frappe/frappe-bench/logs
redis-queue:
image: redis:6.2-alpine
deploy:
restart_policy:
condition: on-failure
volumes:
- redis-queue-data:/data
redis-cache:
image: redis:6.2-alpine
deploy:
restart_policy:
condition: on-failure
volumes:
- redis-cache-data:/data
scheduler:
image: frappe/erpnext:v15.31.3
deploy:
restart_policy:
condition: on-failure
command:
- bench
- schedule
volumes:
- sites:/home/frappe/frappe-bench/sites
- logs:/home/frappe/frappe-bench/logs
websocket:
image: frappe/erpnext:v15.31.3
deploy:
restart_policy:
condition: on-failure
command:
- node
- /home/frappe/frappe-bench/apps/frappe/socketio.js
volumes:
- sites:/home/frappe/frappe-bench/sites
- logs:/home/frappe/frappe-bench/logs
volumes:
db-data:
redis-queue-data:
redis-cache-data:
sites:
logs:
Save and exit: Ctrl+O, Enter, Ctrl+X.
Tip: You can always get the latest version of this file from the official Frappe Docker repository: frappe/frappe_docker — pwd.yml
Step 4 — Start ERPNext
From inside your working directory (~/docker/erpnext), run:
docker compose -p pwd -f pwd.yml up -d
This will:
- Pull all required images from Docker Hub (~1–2 GB total on first run)
- Create the named volumes for data persistence
- Start all services in the background
- Run
configuratorandcreate-siteonce to initialize the database and site
First startup takes 3–10 minutes depending on your internet speed and hardware. The create-site container must complete before the UI becomes available.
Step 5 — Verify the Containers
docker ps -a
Expected output — you should see containers for frontend, backend, websocket, queue-long, queue-short, scheduler, db, redis-cache, and redis-queue.
The configurator and create-site containers will show Exited (0) — this is normal and expected. They are one-time setup containers.
If any persistent service shows Restarting, check its logs:
docker logs <container_name>
Step 6 — Allow Port 8080 (Linux Firewall)
If you are running on a Linux server with ufw enabled:
sudo ufw allow 8080/tcp
If you are running on a cloud VM (GCP, AWS, Azure), also open port 8080 in your cloud firewall/security group settings.
Step 7 — Access ERPNext in Your Browser
Open your browser and go to:
http://localhost:8080
If accessing from another machine on the same network, replace localhost with the host machine's IP address:
http://192.168.x.x:8080
Step 8 — Log In
| Field | Value |
|---|---|
| Username | Administrator
|
| Password | admin
|
After logging in, you will be prompted to complete initial setup (site name, language, timezone, etc.).
To verify the installed version: click Help (top-right) → About.
Day-to-Day Operations
Stop ERPNext
Always stop containers cleanly before shutting down your machine or taking a backup:
docker compose -p pwd -f pwd.yml stop
To stop all running containers system-wide (use with caution):
docker stop $(docker ps -q)
Start ERPNext Again
docker compose -p pwd -f pwd.yml up -d
Use up -d to restart, not start. The up command ensures all dependencies and network links are re-established correctly.
View Live Logs
docker compose -p pwd -f pwd.yml logs -f
To follow logs for a specific service only (e.g., the backend):
docker compose -p pwd -f pwd.yml logs -f backend
Data Persistence
All your ERPNext data (database records, uploaded files, site configuration) is stored in Docker named volumes, not inside the containers. This means:
- Stopping or restarting containers does not delete your data
- Deleting containers with
docker rmdoes not delete your data - Only explicitly removing volumes will delete data
To list volumes:
docker volume ls
To back up the database volume:
docker exec pwd-db-1 mysqldump -u root -padmin --all-databases > erpnext_backup_$(date +%Y%m%d).sql
Nuclear Reset (Start Fresh)
If your setup is broken and you want to wipe everything and start over:
# Stop and remove all containers
docker compose -p pwd -f pwd.yml down
# Remove all containers, volumes, and images (DESTROYS ALL DATA)
docker compose -p pwd -f pwd.yml down -v --rmi all
Or to remove all Docker containers on the system (use with caution):
docker rm -v -f $(docker ps -qa)
After a full reset, rerun Step 4 to start fresh.
Troubleshooting
| Symptom | Likely Cause | Fix |
|---|---|---|
| Browser shows "connection refused" | Containers still starting up | Wait 3–5 more minutes, recheck docker ps
|
frontend container keeps restarting |
Not enough RAM | Free up memory or add swap |
create-site exited with error |
DB not ready in time | Run docker compose -p pwd -f pwd.yml down -v then up -d again
|
| Login works but page is blank | Browser cache issue | Hard refresh (Ctrl+Shift+R)
|
| Changes not saving | Disk full | Check with df -h
|
Next Steps for Students
Once your ERPNext instance is running, here are good things to explore:
- REST API — ERPNext has a built-in REST API. Try:
http://localhost:8080/api/resource/DocType - Webhooks — Configure under Settings → Webhooks to push data on document events
- Custom DocTypes — Build your own data models without writing Python
- Frappe Framework Docs
- ERPNext Docs
- Frappe Docker repo
Original tutorial by Rein Simacon. Updated and expanded 2025.