This setup will share apps and sites folder to host machine so you could explore the code.


  • Clone repository

    git clone

  • Pull image

    docker pull pipech/erpnext-docker-debian:v12-py3-latest

  • Change work directory

    cd erpnext-docker-debian/development_setup

  • Run image using docker-compose (In development folder where docker-compose.yml is)

    docker-compose up -d

  • Find frappe container id

    docker ps -a

  • Call bash in frappe container

    docker exec -it <frappe_container_id> bash

  • Run


  • Start development server

    bench start

  • Go to web browser and access ERPNext


  • Access mysql using MySQL Workbench

User & Password

  • Website

      User : Administrator
      Pass : admin
  • MySQL

      User : root
      Pass : mysql


  • Stop development server, press ctrl +c in terminal

    ctrl + c

  • Exit from container


  • Remove container using docker-compose

    docker-compose down

Create new site

  • Change user and db host from 172.x.x.x to %, it’ll make frappe able to connect to mariadb when frappe change ip

    mysql -h mariadb -uroot -pmysql < "./init.sql"


Has to run this way because of in development setup Apps and Sites folder own by root user when we run bench get-app we will get OSError: [Errno 13] Permission denied error. This is a work around method.

bench new-app erpnext_shopify
cd apps && rm -rf erpnext_shopify
git clone
cd ..
bench install-app erpnext_shopify
bench update --build

Setup SocketIO for development mode

Add sitename to hosts name

ie. site1.loc

Host name location for

  • Windows > C:\Windows\System32\Drivers\etc\hosts
  • Linux & Mac > /etc/hosts

Then SocketIO will works if you access web site using sitename http://site1.loc:8000

Setup auto-reload for development environment

adding reloader_type='stat', to run_simple command in /apps/frappe/frappe/

def serve(port=8000, profile=False, no_reload=False, no_threading=False, site=None, sites_path='.'):
    global application, _site, _sites_path
    _site = site
    _sites_path = sites_path

    from werkzeug.serving import run_simple

    if profile:
            application = ProfilerMiddleware(application, sort_by=('cumtime', 'calls'))

    if not os.environ.get('NO_STATICS'):
            application = SharedDataMiddleware(application, {
                    '/assets': os.path.join(sites_path, 'assets'),

            application = StaticDataMiddleware(application, {
                    '/files': os.path.abspath(sites_path)

    application.debug = True
    application.config = {
            'SERVER_NAME': 'localhost:8000'

    in_test_env = os.environ.get('CI')
    if in_test_env:
            log = logging.getLogger('werkzeug')

    run_simple('', int(port), application,
            use_reloader=False if in_test_env else not no_reload,
            use_debugger=not in_test_env,
            use_evalex=not in_test_env,
            threaded=not no_threading)