How to setup & self-host ERPNext in Docker without Treafik (NGINX only)

Table of contents
ERPNext is an HR and business management platform that can be self-hosted on-premise both directly and via Docker (and even Kubernetes and the like). Though their documentation is 'ok', it's a bit confusing and it's missing some steps for setup.
In this article I will show you the settings, setup files and steps needed to get ERPNext hosted on your own hardware without Treafik.
Why without Treafik? Simple: I don't need it, nor want it and I already have a well configured reverse proxy I use for everything so I don't need yet another tool doing the same thing and causing conflicts.
A few very important notes about my setup here and general important notes:
- While I use it in a production-like environment, I am NOT too picky about perfection.
- I used the built in database setup instead using an existing sql setup. Why? I was lazy. You may remove the MariaDB container here and use your own if you wish, I simply didn't due to laziness.
- I changed the default ports because I have things already running on the original ports suggested.
- Frappe's original docker-compose file did not include port settings for NGINX, and their documentation says NGINX will use port 80. It does not. It uses port 8080, thus I've set up port forwarding to that.
- Also not documented by them (at least no where where I saw), erp-next worker (the
erpnext-python
container) should have port 8000 (container side) available as that's what 'serves' the data to their nginx (which serves it to the public) - In my current setup, I used a specific ERPNext version. They, for some odd reason, initially put using
edge
version which is, what we call here in the business tech world ... a "Bad Idea (tm)". That version rarely worked and we had a lot of problems with it. Use a stable release. Such asstable
orv13
or what I did, I use a specific version:v13.16.0
. Though I'll be soon changing to use thev13
branch to stay up to date. - Use reasonably long but not too long passwords for admin and mysql. Also do NOT use special characters. Someone smarter than me can probably explain, but using special characters and long passwords has always broken setup for me when using docker compose. So, in this setup I've used 27 character long passwords with numbers and letters only.
- I did NOT setup my own docker network for this. Meh. I let Docker & Portainer handle that automatic network creation for me when I did all this through a Portainer stack.
- The docker-compose.yml and .env files have been edited and slimmed down by me in-order to get rid of Treafik and make them work for us.
- You may have to specifically open up the ports on your server so that this will all run.
- In our setup here, we are using an external reverse proxy, so this current setup will NOT work and will NOT be accessible directly. You WILL have to wrap it in reverse proxy in-order to access. In this case we had an existing NGINX centralized and working.
- And yes.... this means you will have TWO NGINX setups ... one here in this docker setup which does the site 'serving' and then your own NGINX (or other reverse proxy) to wrap it in a domain so you can actually access it.
This setup will start eleven (11) containers. One container is there only to initial the system initially and do that kind of work. The container site-creator
will do it's work and then end (stop itself). This is a-ok. Assuming all works well on launch for you, this container will run and depending on your server it will take a while to do the initial setup and migrations. Give it a good while.
First, here is a working ERPNext docker-compose file (docker-compose.yml):
version: "3"
services:
erpnext-nginx:
image: frappe/erpnext-nginx:${ERPNEXT_VERSION}
restart: on-failure
environment:
- FRAPPE_PY=erpnext-python
- FRAPPE_PY_PORT=8000
- FRAPPE_SOCKETIO=frappe-socketio
- SOCKETIO_PORT=9007
- SKIP_NGINX_TEMPLATE_GENERATION=${SKIP_NGINX_TEMPLATE_GENERATION}
ports:
- 8007:8080
volumes:
- sites-vol:/var/www/html/sites:rw
- assets-vol:/assets:rw
erpnext-python:
image: frappe/erpnext-worker:${ERPNEXT_VERSION}
restart: on-failure
environment:
- MARIADB_HOST=${MARIADB_HOST}
- REDIS_CACHE=redis-cache:6379
- REDIS_QUEUE=redis-queue:6379
- REDIS_SOCKETIO=redis-socketio:6379
- SOCKETIO_PORT=9007
- AUTO_MIGRATE=1
- WORKER_CLASS=${WORKER_CLASS}
ports:
- 8000:8000
volumes:
- sites-vol:/home/frappe/frappe-bench/sites:rw
- assets-vol:/home/frappe/frappe-bench/sites/assets:rw
frappe-socketio:
image: frappe/frappe-socketio:${FRAPPE_VERSION}
restart: on-failure
depends_on:
- redis-socketio
ports:
- 9000:9007
- 9007:9007
volumes:
- sites-vol:/home/frappe/frappe-bench/sites:rw
- logs-vol:/home/frappe/frappe-bench/logs:rw
erpnext-worker-default:
image: frappe/erpnext-worker:${ERPNEXT_VERSION}
restart: on-failure
command: worker
depends_on:
- redis-queue
- redis-cache
volumes:
- sites-vol:/home/frappe/frappe-bench/sites:rw
- logs-vol:/home/frappe/frappe-bench/logs:rw
erpnext-worker-short:
image: frappe/erpnext-worker:${ERPNEXT_VERSION}
restart: on-failure
command: worker
environment:
- WORKER_TYPE=short
depends_on:
- redis-queue
- redis-cache
volumes:
- sites-vol:/home/frappe/frappe-bench/sites:rw
- logs-vol:/home/frappe/frappe-bench/logs:rw
erpnext-worker-long:
image: frappe/erpnext-worker:${ERPNEXT_VERSION}
restart: on-failure
command: worker
environment:
- WORKER_TYPE=long
depends_on:
- redis-queue
- redis-cache
volumes:
- sites-vol:/home/frappe/frappe-bench/sites:rw
erpnext-schedule:
image: frappe/erpnext-worker:${ERPNEXT_VERSION}
restart: on-failure
command: schedule
depends_on:
- redis-queue
- redis-cache
volumes:
- sites-vol:/home/frappe/frappe-bench/sites:rw
- logs-vol:/home/frappe/frappe-bench/logs:rw
redis-cache:
image: redis:latest
restart: on-failure
volumes:
- redis-cache-vol:/data
redis-queue:
image: redis:latest
restart: on-failure
volumes:
- redis-queue-vol:/data
redis-socketio:
image: redis:latest
restart: on-failure
volumes:
- redis-socketio-vol:/data
mariadb:
image: mariadb:10.6
restart: on-failure
command:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
- --skip-character-set-client-handshake
- --skip-innodb-read-only-compressed # Temporary fix for MariaDB 10.6
environment:
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
# Sometimes db initialization takes longer than 10 seconds and site-creator goes away.
# Frappe doesn't use CONVERT_TZ() function that requires time zone info, so we can just skip it.
- MYSQL_INITDB_SKIP_TZINFO=1
volumes:
- mariadb-vol:/var/lib/mysql
site-creator:
image: frappe/erpnext-worker:${ERPNEXT_VERSION}
restart: "no"
command: new
depends_on:
- erpnext-python
environment:
- SITE_NAME=${SITE_NAME}
- DB_ROOT_USER=${DB_ROOT_USER}
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
- ADMIN_PASSWORD=${ADMIN_PASSWORD}
- INSTALL_APPS=${INSTALL_APPS}
volumes:
- sites-vol:/home/frappe/frappe-bench/sites:rw
- logs-vol:/home/frappe/frappe-bench/logs:rw
volumes:
mariadb-vol:
redis-cache-vol:
redis-queue-vol:
redis-socketio-vol:
assets-vol:
sites-vol:
cert-vol:
logs-vol:
Here is a slimmed down ERPNext env file you can use (in Portainer you can add them line by line or use the advanced editor button when creating a stack to just copy-paste this in after making your changes)
Quick note about the .env file: the 'SITES' variables HAS TO have the url in back quotes ==> ` <<==
ERPNEXT_VERSION=v13
FRAPPE_VERSION=v13
MARIADB_HOST=mariadb
MYSQL_ROOT_PASSWORD=a_mysql_Passw0rd_CHANGEME
SITE_NAME=my.mywebsite.com
SITES=`my.mywebsite.com`
DB_ROOT_USER=root
ADMIN_PASSWORD=myfancyadminPasswordCHANGEME
INSTALL_APPS=erpnext
SKIP_NGINX_TEMPLATE_GENERATION=0
WORKER_CLASS=gthread
Toss the above into a Portainer stack or via command line, and it'll set itself all up. Of course, change the emails, passwords and url's and ports as needed for your own unique setup.