Skip to content

Instantly share code, notes, and snippets.

@whinee
Last active March 25, 2025 18:35
Show Gist options
  • Save whinee/75a78d1cc6d318c696496c1603dd9955 to your computer and use it in GitHub Desktop.
Save whinee/75a78d1cc6d318c696496c1603dd9955 to your computer and use it in GitHub Desktop.
Example of running Frappe's ERPNext using a Docker Compose .yaml file

Introduction

Running Frappe's ERPNext using a Docker Compose .yaml file is a confusing and, for me, a frustrating endeavour.

What I hope this gist will acheive is to enlighten others as well on how to run said wonderful software in their own servers.

In this example, we will be using Portainer to run the docker compose file, and store Frappe's data in the base directory /root/data-program/frappe.

This method requires running a bit of shell script, which is risky. But the script is easily readable and is only a few short lines. Feel free to poke around though, as it is highly encouraged, for your own sake as well.

Prerequisites

You need to have Portainer installed in your server. You need a bit of patience as well. Though, I hope that I will save you a lot of debugging through this gist.

You then need to download frappe-init.sh (attached below) to your server.

Initialize Directories

Caution

The following commands will erase the directory and its contents that you give to said command

Run the following command to make the base directory and subdirectories where Frappe's data will be stored:

bash frappe-init.sh <base_directory>

Such that storing Frappe's data, you need to run the following command:

bash frappe-init.sh /root/data-program/frappe

Modifying the docker compose .yaml file

I'm sorry, but you still have to modify the docker compose .yaml file. But only a bit!

You only need to replace two things in said file:

  • [base_directory]: The base directory used above.
  • [port]: The port at which you are gonna be accessing your instance locally.

And now, you are ready to go! Go run that thing :3

Explanation

So, what changed from the original docker compose .yaml file? Actually, now that I've looked at the diff between the original one and my own version, nothing has changed.

So what was the problem that I was trying to solve?

I don't actually remember anymore exactly. What I remember though is that I am having network issues. And then, I tried the solution in this Github issue comment, which does not work for my purposes (See Observations section below)

Ahh, the joys of debugging technology. It looks like I'm the dumb one in here!

Observations

As suggested in this Github issue comment, you need to do the following to solve the network issues:

  1. set the docker compose .yaml file's services.frontend.environment.FRAPPE_SITE_NAME_HEADER's value to $$host;
  2. set the docker compose .yaml file's services.create-site.command's last line to have the command's --set-default option to socketsite.localhost

However, if you need to access the site in other than socketsite.localhost, such as the server's IP (an example of is 192.168.1.20), then it would not work.

If you want to use those settings, then I guess we could make it work. The way I have made it work is by doing these things:

  1. set the docker compose .yaml file's services.create-site.command's last line to have the command's --set-default option to the base URL from where you want to access the site. ex.: 192.168.1.20 or frappe.localhost.com.
  2. move [sites]/socketsite.localhost to [sites]/[base_URL], whereas:
    1. [sites] is where the docker volume sites is located at. In my config, it is located at /root/data-program/frappe/sites.
    2. [base_URL] is the base URL from where you want to access the site. ex.: 192.168.1.20 or frappe.localhost.com.

And there you have it!

I think the app works as is though? I don't even know what I was trying to solve in the first place LMFAOOOO

Last Thoughts

I am stupid.

if [ -z "$1" ]; then
echo "Usage: bash frappe-init.sh <base_directory>"
return 1
fi
BASE_DIR="$1"
rm -rf "$BASE_DIR"
mkdir -p "$BASE_DIR"/{assets,db-data,logs,redis-cache-data,redis-queue-data,sites}
chmod -R 0777 "$BASE_DIR"
echo "Frappe directories set up in: $BASE_DIR"
services:
backend:
image: frappe/erpnext:v15.55.0
networks:
- frappe_network
deploy:
restart_policy:
condition: on-failure
volumes:
- sites:/home/frappe/frappe-bench/sites
- logs:/home/frappe/frappe-bench/logs
- assets:/home/frappe/frappe-bench/sites/assets
environment:
DB_HOST: db
DB_PORT: "3306"
MYSQL_ROOT_PASSWORD: admin
MARIADB_ROOT_PASSWORD: admin
configurator:
image: frappe/erpnext:v15.55.0
networks:
- frappe_network
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
- assets:/home/frappe/frappe-bench/sites/assets
create-site:
image: frappe/erpnext:v15.55.0
networks:
- frappe_network
deploy:
restart_policy:
condition: none
volumes:
- sites:/home/frappe/frappe-bench/sites
- logs:/home/frappe/frappe-bench/logs
- assets:/home/frappe/frappe-bench/sites/assets
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 --mariadb-user-host-login-scope='%' --admin-password=admin --db-root-username=root --db-root-password=admin --install-app erpnext --set-default frontend;
db:
image: mariadb:10.6
networks:
- frappe_network
healthcheck:
test: mysqladmin ping -h localhost --password=admin
interval: 1s
retries: 20
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 # Temporary fix for MariaDB 10.6
environment:
MYSQL_ROOT_PASSWORD: admin
MARIADB_ROOT_PASSWORD: admin
volumes:
- db-data:/var/lib/mysql
frontend:
image: frappe/erpnext:v15.55.0
networks:
- frappe_network
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
- assets:/home/frappe/frappe-bench/sites/assets
ports:
- "[port]:8080"
queue-long:
image: frappe/erpnext:v15.55.0
networks:
- frappe_network
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
- assets:/home/frappe/frappe-bench/sites/assets
queue-short:
image: frappe/erpnext:v15.55.0
networks:
- frappe_network
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
- assets:/home/frappe/frappe-bench/sites/assets
redis-queue:
image: redis:6.2-alpine
networks:
- frappe_network
deploy:
restart_policy:
condition: on-failure
volumes:
- redis-queue-data:/data
redis-cache:
image: redis:6.2-alpine
networks:
- frappe_network
deploy:
restart_policy:
condition: on-failure
volumes:
- assets:/home/frappe/frappe-bench/sites/assets
- redis-cache-data:/data
scheduler:
image: frappe/erpnext:v15.55.0
networks:
- frappe_network
deploy:
restart_policy:
condition: on-failure
command:
- bench
- schedule
volumes:
- sites:/home/frappe/frappe-bench/sites
- logs:/home/frappe/frappe-bench/logs
- assets:/home/frappe/frappe-bench/sites/assets
websocket:
image: frappe/erpnext:v15.55.0
networks:
- frappe_network
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
- assets:/home/frappe/frappe-bench/sites/assets
volumes:
assets:
driver_opts:
type: none
o: bind
device: [base_directory]/assets
db-data:
driver_opts:
type: none
o: bind
device: [base_directory]/db-data
redis-cache-data:
driver_opts:
type: none
o: bind
device: [base_directory]/redis-cache-data
redis-queue-data:
driver_opts:
type: none
o: bind
device: [base_directory]/redis-queue-data
sites:
driver_opts:
type: none
o: bind
device: [base_directory]/sites
logs:
driver_opts:
type: none
o: bind
device: [base_directory]/logs
networks:
frappe_network:
driver: bridge
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment