ERPNext with India Compliance
Install ERPNext v15 with India Compliance v15 app on docker.
Note: the same steps work for ERPNext v16 also, just replace 15 with 16 in all the below config files, sed -i 's/15/16/g' *.sh *.yaml Containerfile.
Steps
- Use the below
build.shto create a custom docker container with ERPNext and India Compliance app installed. - After
build.shcompletes, checkdocker imagesand delete any temporary images created during the build. - Use the below
docker-compose.yamlandrun.shto start the custom docker container and dependencies. The docker-compose.yaml shown here changes all docker volumes to bind mounts (but docker-compose still creates volumes for some reason). - Login to ERPNext using http://$IP:8080 with default username
Administratorand passwordadmin.
Filename: build.sh
#!/bin/bash
# Filename: build.sh
# Build a custom docker image with ERPNext and India Compliance v15 apps
# Run `docker images` and delete any temporary images created during the build process
set -eoux pipefail
export APPS_JSON='[
{
"url": "https://github.com/frappe/erpnext",
"branch": "version-15"
},
{
"url": "https://github.com/resilient-tech/india-compliance",
"branch": "version-15"
}
]'
export APPS_JSON_BASE64=$(echo ${APPS_JSON} | base64 -w 0)
wget https://raw.githubusercontent.com/frappe/frappe_docker/refs/heads/main/images/layered/Containerfile
docker build \
--build-arg=FRAPPE_BRANCH=version-15 \
--build-arg=APPS_JSON_BASE64=$APPS_JSON_BASE64 \
--tag=erpnext-custom:15 \
--file=Containerfile .Filename: run.sh
#!/bin/bash
# Filename: run.sh
set -eoux pipefail
mkdir sites logs
chown 1000:1000 sites logs
cat << EOF > sites/common_site_config.json
{
"db_type": "mariadb",
"db_name": "frontend",
"db_password": "$(openssl rand -base64 12)"
}
EOF
chown 1000:1000 sites/common_site_config.json
docker-compose up -d
echo Waiting for ERPNext installation to complete...
sleep 120
echo Installing india_compliance app
docker-compose exec frontend bench --site frontend install-app india_complianceFilename: Containerfile
# Filename: Containerfile
# Adapted from: https://github.com/frappe/frappe_docker/blob/main/images/layered/Containerfile
ARG FRAPPE_BRANCH=version-15
FROM frappe/build:${FRAPPE_BRANCH} AS builder
ARG FRAPPE_BRANCH=version-15
ARG FRAPPE_PATH=https://github.com/frappe/frappe
ARG APPS_JSON_BASE64
USER root
RUN if [ -n "${APPS_JSON_BASE64}" ]; then \
mkdir /opt/frappe && echo "${APPS_JSON_BASE64}" | base64 -d > /opt/frappe/apps.json; \
fi
USER frappe
RUN export APP_INSTALL_ARGS="" && \
if [ -n "${APPS_JSON_BASE64}" ]; then \
export APP_INSTALL_ARGS="--apps_path=/opt/frappe/apps.json"; \
fi && \
bench init ${APP_INSTALL_ARGS}\
--frappe-branch=${FRAPPE_BRANCH} \
--frappe-path=${FRAPPE_PATH} \
--no-procfile \
--no-backups \
--skip-redis-config-generation \
--verbose \
/home/frappe/frappe-bench && \
cd /home/frappe/frappe-bench && \
echo "{}" > sites/common_site_config.json && \
find apps -mindepth 1 -path "*/.git" | xargs rm -fr
FROM frappe/base:${FRAPPE_BRANCH} AS backend
USER frappe
COPY --from=builder --chown=frappe:frappe /home/frappe/frappe-bench /home/frappe/frappe-bench
WORKDIR /home/frappe/frappe-bench
VOLUME [ \
"/home/frappe/frappe-bench/sites", \
"/home/frappe/frappe-bench/sites/assets", \
"/home/frappe/frappe-bench/logs" \
]
CMD [ \
"/home/frappe/frappe-bench/env/bin/gunicorn", \
"--chdir=/home/frappe/frappe-bench/sites", \
"--bind=0.0.0.0:8000", \
"--threads=4", \
"--workers=2", \
"--worker-class=gthread", \
"--worker-tmp-dir=/dev/shm", \
"--timeout=120", \
"--preload", \
"frappe.app:application" \
]Filename: docker-compose.yaml
# Filename: docker-compose.yaml
# Adapted from: https://github.com/frappe/frappe_docker/blob/main/pwd.yml
# Replaced docker volumes with bind mounts
# Replaced original erpnext image with custom image that includes India Compliance app
services:
backend:
image: erpnext-custom:15
networks:
- frappe_network
deploy:
restart_policy:
condition: on-failure
volumes:
- ./sites:/home/frappe/frappe-bench/sites
- ./logs:/home/frappe/frappe-bench/logs
environment:
DB_HOST: db
DB_PORT: "3306"
MYSQL_ROOT_PASSWORD: admin
MARIADB_ROOT_PASSWORD: admin
configurator:
image: erpnext-custom:15
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
create-site:
image: erpnext-custom:15
networks:
- frappe_network
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 --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: erpnext-custom:15
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
ports:
- "8080:8080"
queue-long:
image: erpnext-custom:15
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
environment:
FRAPPE_REDIS_CACHE: redis://redis-cache:6379
FRAPPE_REDIS_QUEUE: redis://redis-queue:6379
queue-short:
image: erpnext-custom:15
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
environment:
FRAPPE_REDIS_CACHE: redis://redis-cache:6379
FRAPPE_REDIS_QUEUE: redis://redis-queue:6379
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
scheduler:
image: erpnext-custom:15
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
websocket:
image: erpnext-custom:15
networks:
- frappe_network
deploy:
restart_policy:
condition: on-failure
command:
- node
- /home/frappe/frappe-bench/apps/frappe/socketio.js
environment:
FRAPPE_REDIS_CACHE: redis://redis-cache:6379
FRAPPE_REDIS_QUEUE: redis://redis-queue:6379
volumes:
- ./sites:/home/frappe/frappe-bench/sites
- ./logs:/home/frappe/frappe-bench/logs
networks:
frappe_network:
driver: bridgeReferences
- India Compliance app docker installation: https://docs.indiacompliance.app/docs/getting-started/installation#docker
- ERPNext single compose setup: https://github.com/frappe/frappe_docker/blob/main/docs/01-getting-started/02-single-compose-setup.md
Bugs
docker volume lsshows volumes created forsites,logsetc. even though bind mounts are used indocker-compose.yaml.- Removing
VOLUMEsection fromContainerfilestill docker volumes.