├── railway ├── README.md ├── railway-entrypoint.sh ├── railway-cmd.sh ├── railway-setup.sh ├── temp_nginx.conf ├── temp_supervisor.conf └── Dockerfile ├── .gitignore ├── docs └── assets │ └── railway-frappe-arch.excalidraw.png ├── setup_development └── .devcontainer │ ├── devcontainer.json │ └── docker-compose.yml ├── railway.json ├── entrypoint.sh ├── mariadb.cnf ├── LICENSE ├── scripts └── tag_image.py ├── .github └── workflows │ └── push-docker.yml ├── README.md └── Dockerfile /railway/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | !/.gitignore 2 | /development_setup/data/ 3 | /docs/_site/ 4 | /.vscode 5 | !/.devcontainer* -------------------------------------------------------------------------------- /docs/assets/railway-frappe-arch.excalidraw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pipech/erpnext-docker-debian/HEAD/docs/assets/railway-frappe-arch.excalidraw.png -------------------------------------------------------------------------------- /setup_development/.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frappe", 3 | "service": "frappe", 4 | "dockerComposeFile": "docker-compose.yml", 5 | "workspaceFolder": "/home/frappe/bench/apps" 6 | } 7 | -------------------------------------------------------------------------------- /railway.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://railway.com/railway.schema.json", 3 | "build": { 4 | "builder": "DOCKERFILE", 5 | "dockerfilePath": "/railway/Dockerfile" 6 | }, 7 | "deploy": { 8 | "numReplicas": 1 9 | } 10 | } -------------------------------------------------------------------------------- /entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | { 3 | sudo service mariadb start 4 | bench start 5 | } || { 6 | echo "=============================================" 7 | echo "ERROR: entrypoint command failed to start" 8 | echo "=============================================" 9 | tail -f /dev/null 10 | } -------------------------------------------------------------------------------- /setup_development/.devcontainer/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | frappe: 5 | image: pipech/erpnext-docker-debian:15-F1.0_E2.0 6 | ports: 7 | - '8000:8000' #webserver_port 8 | - '9000:9000' #socketio_port 9 | - '3306:3306' #mysql_port 10 | command: sleep infinity 11 | -------------------------------------------------------------------------------- /railway/railway-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | echo "-> Set ownership of sites folder" 5 | chown frappe:frappe /home/frappe/bench/sites 6 | 7 | echo "-> Linking assets" 8 | su frappe -c "ln -sf /home/frappe/bench/built_sites/assets /home/frappe/bench/sites/assets" 9 | su frappe -c "ln -sf /home/frappe/bench/built_sites/apps.json /home/frappe/bench/sites/apps.json" 10 | su frappe -c "ln -sf /home/frappe/bench/built_sites/apps.txt /home/frappe/bench/sites/apps.txt" 11 | 12 | exec "$@" 13 | -------------------------------------------------------------------------------- /railway/railway-cmd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | echo "-> Clearing cache" 5 | su frappe -c "bench execute frappe.cache_manager.clear_global_cache" 6 | 7 | echo "-> Bursting env into config" 8 | envsubst '$RFP_DOMAIN_NAME' < /home/$systemUser/temp_nginx.conf > /etc/nginx/conf.d/default.conf 9 | envsubst '$PATH,$HOME,$NVM_DIR,$NODE_VERSION' < /home/$systemUser/temp_supervisor.conf > /home/$systemUser/supervisor.conf 10 | 11 | echo "-> Starting nginx" 12 | nginx 13 | 14 | echo "-> Starting supervisor" 15 | /usr/bin/supervisord -c /home/$systemUser/supervisor.conf 16 | -------------------------------------------------------------------------------- /railway/railway-setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # -> Run entrypoint 5 | # somehow when specify custom cmd in railway, 6 | # it doesn't run entrypoint first, so we need to run it here. 7 | sudo /usr/local/bin/railway-entrypoint.sh 8 | 9 | echo "-> Create empty common site config" 10 | echo "{}" > /home/frappe/bench/sites/common_site_config.json 11 | 12 | echo "-> Create new site with ERPNext" 13 | bench new-site ${RFP_DOMAIN_NAME} --admin-password ${RFP_SITE_ADMIN_PASSWORD} --no-mariadb-socket --db-root-password ${RFP_DB_ROOT_PASSWORD} --install-app erpnext 14 | bench use ${RFP_DOMAIN_NAME} 15 | 16 | echo "-> Enable scheduler" 17 | bench enable-scheduler 18 | -------------------------------------------------------------------------------- /mariadb.cnf: -------------------------------------------------------------------------------- 1 | # MariaDB-specific config file. 2 | # Read by /etc/mysql/my.cnf 3 | 4 | 5 | [mysql] 6 | default-character-set = utf8mb4 7 | 8 | 9 | [client] 10 | # Default is Latin1, if you need UTF-8 set this (also in server section) 11 | # default-character-set = utf8 12 | 13 | 14 | [mysqld] 15 | # Character sets 16 | # Default is Latin1, if you need UTF-8 set all this (also in client section) 17 | # character-set-server = utf8 18 | # collation-server = utf8_general_ci 19 | 20 | # Import all .cnf files from configuration directory 21 | !includedir /etc/mysql/mariadb.conf.d/ 22 | 23 | bind-address = 0.0.0.0 24 | 25 | character-set-client-handshake = FALSE 26 | character-set-server = utf8mb4 27 | collation-server = utf8mb4_unicode_ci 28 | 29 | 30 | [mysqld_safe] 31 | skip_log_error 32 | syslog 33 | 34 | 35 | [mysqldump] 36 | quick 37 | quote-names 38 | max_allowed_packet = 16M 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 pipech 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /scripts/tag_image.py: -------------------------------------------------------------------------------- 1 | # Script for: .github/workflows/push-docker.yml 2 | 3 | import json 4 | import os 5 | 6 | 7 | def read_version_from_file(file_path): 8 | """ 9 | Data from file should be some thing like this: 10 | erpnext 12.10.1 11 | frappe 12.8.1 12 | 13 | :param file_path: path to file 14 | :return: dict of app version 15 | { 16 | 'erpnext': '12.10.1', 17 | 'frappe': '12.8.1', 18 | } 19 | """ 20 | values = {} 21 | with open(file_path, 'r') as file: 22 | for line in file: 23 | parts = line.split() 24 | if len(parts) >= 2: 25 | key = parts[0] 26 | value = parts[1] 27 | values[key] = value 28 | return values 29 | 30 | 31 | def get_app_version(apps_version): 32 | """ 33 | :param apps_version: dict of app version 34 | { 35 | 'erpnext': '12.10.1', 36 | 'frappe': '12.8.1', 37 | } 38 | :return: version tag string 39 | 12-F10.1_E14.3 40 | """ 41 | e = apps_version['erpnext'].split('.') 42 | f = apps_version['frappe'].split('.') 43 | 44 | # construct version tag 45 | # 12-F10.1_E14.3 46 | version = '{major}-F{frappe_minor}.{frappe_patch}_E{erpnext_minor}.{erpnext_patch}'.format( 47 | major=f[0], 48 | frappe_minor=f[1], 49 | frappe_patch=f[2], 50 | erpnext_minor=e[1], 51 | erpnext_patch=e[2], 52 | ) 53 | 54 | return version 55 | 56 | def main(): 57 | print('>>> Prepare image tagging') 58 | version_dict = read_version_from_file('version.txt') 59 | print(f'> Raw app version: {json.dumps(version_dict)}') 60 | app_version = get_app_version(version_dict) 61 | print(f'> App version: {app_version}') 62 | 63 | # Print the command to set the new tag in the GitHub Actions environment variable 64 | os.system(f"echo \"IMAGE_VERSION_TAG={app_version}\" >> $GITHUB_ENV") 65 | 66 | 67 | if __name__ == "__main__": 68 | main() -------------------------------------------------------------------------------- /railway/temp_nginx.conf: -------------------------------------------------------------------------------- 1 | upstream bench-frappe { 2 | server 127.0.0.1:8000 fail_timeout=0; 3 | } 4 | 5 | upstream bench-socketio-server { 6 | server 127.0.0.1:9000 fail_timeout=0; 7 | } 8 | 9 | server { 10 | listen 80; 11 | server_name ${RFP_DOMAIN_NAME}; 12 | root /home/frappe/bench/sites; 13 | 14 | location /assets { 15 | try_files $uri =404; 16 | } 17 | 18 | location ~ ^/protected/(.*) { 19 | internal; 20 | try_files /$host/$1 =404; 21 | } 22 | 23 | location /socket.io { 24 | proxy_http_version 1.1; 25 | proxy_set_header Upgrade $http_upgrade; 26 | proxy_set_header Connection "upgrade"; 27 | proxy_set_header X-Frappe-Site-Name $host; 28 | proxy_set_header Origin $scheme://$http_host; 29 | proxy_set_header Host $host; 30 | 31 | proxy_pass http://bench-socketio-server; 32 | } 33 | 34 | location / { 35 | try_files /$host/public/$uri @webserver; 36 | } 37 | 38 | location @webserver { 39 | proxy_set_header X-Robots-Tag noindex; 40 | 41 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 42 | proxy_set_header X-Forwarded-Proto $scheme; 43 | proxy_set_header X-Frappe-Site-Name $host; 44 | proxy_set_header Host $host; 45 | proxy_set_header X-Use-X-Accel-Redirect True; 46 | proxy_read_timeout 120; 47 | proxy_redirect off; 48 | 49 | proxy_pass http://bench-frappe; 50 | } 51 | 52 | # error pages 53 | error_page 502 /502.html; 54 | location /502.html { 55 | root /home/frappe/bench-repo/bench/config/templates; 56 | internal; 57 | } 58 | 59 | # optimizations 60 | sendfile on; 61 | keepalive_timeout 15; 62 | client_max_body_size 50m; 63 | client_body_buffer_size 16K; 64 | client_header_buffer_size 1k; 65 | 66 | # enable gzip compresion 67 | # based on https://mattstauffer.co/blog/enabling-gzip-on-nginx-servers-including-laravel-forge 68 | # text/html is always compressed by HttpGzipModule 69 | gzip on; 70 | gzip_http_version 1.1; 71 | gzip_comp_level 5; 72 | gzip_min_length 256; 73 | gzip_proxied any; 74 | gzip_vary on; 75 | gzip_types 76 | application/atom+xml 77 | application/javascript 78 | application/json 79 | application/rss+xml 80 | application/vnd.ms-fontobject 81 | application/x-font-ttf 82 | application/font-woff 83 | application/x-web-app-manifest+json 84 | application/xhtml+xml 85 | application/xml 86 | font/opentype 87 | image/svg+xml 88 | image/x-icon 89 | text/css 90 | text/plain 91 | text/x-component 92 | ; 93 | } 94 | -------------------------------------------------------------------------------- /.github/workflows/push-docker.yml: -------------------------------------------------------------------------------- 1 | name: build-tag-push 2 | 3 | on: 4 | schedule: 5 | # At 00:00 on Sunday. 6 | - cron: '0 0 * * 0' 7 | push: 8 | branches: 9 | - master 10 | 11 | env: 12 | IMAGE_REPO: pipech 13 | IMAGE_NAME: erpnext-docker-debian 14 | 15 | jobs: 16 | build-tag-push: 17 | runs-on: ubuntu-latest 18 | strategy: 19 | matrix: 20 | app_branch: [version-14, version-15] 21 | steps: 22 | - name: Generate action image action tag 23 | run: echo "IMAGE_ACTION_TAG=${{ env.IMAGE_REPO }}/${{ env.IMAGE_NAME }}:${{ github.actor_id }}" >> $GITHUB_ENV 24 | 25 | - name: Checkout 26 | uses: actions/checkout@v4 27 | 28 | - name: Set up QEMU 29 | uses: docker/setup-qemu-action@v3 30 | 31 | - name: Set up Docker Buildx 32 | uses: docker/setup-buildx-action@v3 33 | 34 | - name: Login to Docker Hub 35 | uses: docker/login-action@v3 36 | with: 37 | username: ${{ secrets.DOCKERHUB_USERNAME }} 38 | password: ${{ secrets.DOCKERHUB_TOKEN }} 39 | 40 | - name: Build and export to Docker 41 | uses: docker/build-push-action@v5 42 | with: 43 | context: . 44 | load: true 45 | tags: ${{ env.IMAGE_ACTION_TAG }} 46 | build-args: | 47 | appBranch=${{ matrix.app_branch }} 48 | 49 | - name: Start container 50 | run: | 51 | docker run -d -p 8000:8000 -p 9000:9000 --name "TEST_CONTAINER" "${{ env.IMAGE_ACTION_TAG }}" 52 | 53 | - name: Wait for 2 minutes and check status 54 | run: | 55 | sleep 180s 56 | docker ps -a 57 | docker logs TEST_CONTAINER 58 | docker inspect TEST_CONTAINER 59 | 60 | - name: Check HTTP Response 61 | run: | 62 | response=$(curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:8000) 63 | if [ "$response" == 200 ]; then 64 | echo "Received a 200 response!" 65 | else 66 | echo "Failed to receive a 200 response, got: $response" 67 | exit 1 68 | fi 69 | 70 | - name: Getting app version from image 71 | run: | 72 | docker exec TEST_CONTAINER bench version > version.txt 73 | 74 | - name: Formatting and set version variable 75 | run: python ./scripts/tag_image.py 76 | 77 | - name: Tag and push 78 | uses: docker/build-push-action@v5 79 | with: 80 | context: . 81 | push: true 82 | tags: ${{ env.IMAGE_REPO }}/${{ env.IMAGE_NAME }}:${{ env.IMAGE_VERSION_TAG }},${{ env.IMAGE_REPO }}/${{ env.IMAGE_NAME }}:${{ matrix.app_branch }}-latest 83 | -------------------------------------------------------------------------------- /railway/temp_supervisor.conf: -------------------------------------------------------------------------------- 1 | [program:bench-frappe-web] 2 | command=/home/frappe/bench/env/bin/gunicorn frappe.app:application --bind 0.0.0.0:8000 --worker-class=gthread --workers=1 --threads=2 --timeout 120 --graceful-timeout 30 --preload 3 | priority=2 4 | autostart=true 5 | autorestart=true 6 | stderr_logfile=/home/frappe/bench/logs/web.error.log 7 | user=frappe 8 | directory=/home/frappe/bench/sites 9 | 10 | [program:bench-node-socketio] 11 | command=${NVM_DIR}/versions/node/v${NODE_VERSION}/bin/node /home/frappe/bench/apps/frappe/socketio.js 12 | priority=2 13 | autostart=true 14 | autorestart=true 15 | stderr_logfile=/home/frappe/bench/logs/node-socketio.error.log 16 | user=frappe 17 | directory=/home/frappe/bench 18 | 19 | [group:bench-web] 20 | programs=bench-frappe-web,bench-node-socketio 21 | 22 | [program:bench-frappe-default-worker] 23 | command=/home/frappe/.local/bin/bench worker --queue default 24 | priority=4 25 | autostart=true 26 | autorestart=true 27 | stdout_logfile=/home/frappe/bench/logs/worker.log 28 | stderr_logfile=/home/frappe/bench/logs/worker.error.log 29 | user=frappe 30 | stopwaitsecs=1560 31 | directory=/home/frappe/bench 32 | killasgroup=true 33 | numprocs=3 34 | process_name=%(program_name)s-%(process_num)d 35 | environment=PATH="${PATH}",HOME="/home/frappe" 36 | 37 | [program:bench-frappe-short-worker] 38 | command=/home/frappe/.local/bin/bench worker --queue short 39 | priority=4 40 | autostart=true 41 | autorestart=true 42 | stdout_logfile=/home/frappe/bench/logs/worker.log 43 | stderr_logfile=/home/frappe/bench/logs/worker.error.log 44 | user=frappe 45 | stopwaitsecs=360 46 | directory=/home/frappe/bench 47 | killasgroup=true 48 | numprocs=3 49 | process_name=%(program_name)s-%(process_num)d 50 | environment=PATH="${PATH}",HOME="/home/frappe" 51 | 52 | [program:bench-frappe-long-worker] 53 | command=/home/frappe/.local/bin/bench worker --queue long 54 | priority=4 55 | autostart=true 56 | autorestart=true 57 | stdout_logfile=/home/frappe/bench/logs/worker.log 58 | stderr_logfile=/home/frappe/bench/logs/worker.error.log 59 | user=frappe 60 | stopwaitsecs=1560 61 | directory=/home/frappe/bench 62 | killasgroup=true 63 | numprocs=3 64 | process_name=%(program_name)s-%(process_num)d 65 | environment=PATH="${PATH}",HOME="/home/frappe" 66 | 67 | [program:bench-frappe-schedule] 68 | command=/home/frappe/.local/bin/bench schedule 69 | priority=3 70 | autostart=true 71 | autorestart=true 72 | stdout_logfile=/home/frappe/bench/logs/schedule.log 73 | stderr_logfile=/home/frappe/bench/logs/schedule.error.log 74 | user=frappe 75 | directory=/home/frappe/bench 76 | environment=PATH="${PATH}",HOME="/home/frappe" 77 | 78 | [group:bench-workers] 79 | programs=bench-frappe-default-worker,bench-frappe-short-worker,bench-frappe-long-worker,bench-frappe-schedule 80 | 81 | [supervisord] 82 | nodaemon=true 83 | user=root 84 | 85 | [supervisorctl] -------------------------------------------------------------------------------- /railway/Dockerfile: -------------------------------------------------------------------------------- 1 | # ------------------------------------------ 2 | # - Stage: 01 - builder 3 | # ------------------------------------------ 4 | 5 | # This image is also based on frappe/bench which we use for production 6 | # https://github.com/pipech/erpnext-docker-debian/blob/master/Dockerfile 7 | FROM pipech/erpnext-docker-debian:version-15-latest AS builder 8 | 9 | # These variable has been set from base image 10 | # $systemUser, $benchFolderName 11 | USER $systemUser 12 | WORKDIR /home/$systemUser/$benchFolderName 13 | 14 | RUN echo "-> Start builder" \ 15 | ### remove unused sites, created by base image 16 | && rm -rf /home/$systemUser/$benchFolderName/sites/site1.local \ 17 | ### [HOTFIX] Railway used IPv6 (Does not support IPv4), https://docs.railway.com/guides/private-networking#caveats 18 | ### https://github.com/frappe/frappe/issues/33981 19 | && sed -i 's/socket\.AF_INET, socket\.SOCK_STREAM/socket.AF_INET6, socket.SOCK_STREAM/g' /home/frappe/bench/apps/frappe/frappe/utils/connections.py \ 20 | ### install your custom apps here 21 | # && bench get-app https://github.com/your-repo/custom_app 22 | && echo "-> Builder done!" 23 | 24 | 25 | # ------------------------------------------ 26 | # - Stage: 02 - production 27 | # ------------------------------------------ 28 | 29 | # Image version should also match with stage 01 30 | # (Check pipech/erpnext-docker-debian Dockerfile) 31 | # https://github.com/frappe/frappe_docker/blob/main/images/bench/Dockerfile 32 | FROM frappe/bench:v5.22.9 33 | 34 | # this env should match stage 01 35 | # (Check pipech/erpnext-docker-debian Dockerfile) 36 | ENV systemUser=frappe 37 | ENV benchFolderName=bench 38 | 39 | # Copied bench folder from build stage 40 | COPY --from=builder --chown=$systemUser /home/$systemUser/$benchFolderName /home/$systemUser/$benchFolderName 41 | 42 | # Copy template 43 | COPY /railway/temp_nginx.conf /home/$systemUser/temp_nginx.conf 44 | COPY /railway/temp_supervisor.conf /home/$systemUser/temp_supervisor.conf 45 | 46 | USER root 47 | WORKDIR /home/$systemUser/$benchFolderName 48 | 49 | # [fix] "debconf: unable to initialize frontend: Dialog" 50 | # https://github.com/moby/moby/issues/27988 51 | ARG DEBIAN_FRONTEND=noninteractive 52 | 53 | RUN echo "-> Install nginx & supervisor" \ 54 | # apt-get update 55 | && apt-get update \ 56 | && apt-get install -y \ 57 | # nginx for serving files 58 | nginx \ 59 | # supervisor to run multiple server per container 60 | supervisor \ 61 | && echo "-> Cleaning installation" \ 62 | && apt-get clean \ 63 | && rm -rf /var/lib/apt/lists/* \ 64 | && echo "-> Remove nginx default site" \ 65 | && rm /etc/nginx/sites-enabled/default \ 66 | && echo "-> Rebuilding bench" \ 67 | && su $systemUser -c "bench build" \ 68 | && su $systemUser -c "cp -r /home/$systemUser/$benchFolderName/sites /home/$systemUser/$benchFolderName/built_sites" 69 | 70 | COPY --chown=$systemUser --chmod=0755 /railway/railway-setup.sh /home/$systemUser/$benchFolderName/railway-setup.sh 71 | COPY --chown=$systemUser --chmod=0755 /railway/railway-entrypoint.sh /usr/local/bin/railway-entrypoint.sh 72 | COPY --chown=$systemUser --chmod=0755 /railway/railway-cmd.sh /usr/local/bin/railway-cmd.sh 73 | 74 | ENTRYPOINT ["/usr/local/bin/railway-entrypoint.sh"] 75 | CMD ["/usr/local/bin/railway-cmd.sh"] 76 | 77 | EXPOSE 80 78 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # :whale: ERPNext on Docker 2 | 3 | ![push-docker](https://github.com/pipech/erpnext-docker-debian/actions/workflows/push-docker.yml/badge.svg) 4 | 5 | **This repository prioritizes stability, repeatability and simplicity, and is not designed as the ideal approach for a production environment.** 6 | 7 | [![Deploy on Railway](https://railway.com/button.svg)](https://railway.com/deploy/gfm6Se?referralCode=0XIWba&utm_medium=integration&utm_source=template&utm_campaign=generic) 8 | 9 | ## Problem 10 | 11 | - ERPNext development progresses rapidly, with new updates released daily. Some of these updates may contain bugs. 12 | 13 | - ERPNext depends on numerous external components. During installation, these dependencies can sometimes cause issues, and without a pre-built image, it might be impossible to build or use older versions. 14 | 15 | ## Solution 16 | 17 | By using Docker, we can pre-build images and push them to [Docker hub](https://hub.docker.com/r/pipech/erpnext-docker-debian/). This ensures that usable images are always available, and you can select the version that best suits your needs. 18 | 19 | ## Usage 20 | 21 | ### Trial Setup 22 | 23 | This setup is designed for users who want to explore the system and is not suitable for production use. 24 | 25 | ```sh 26 | docker pull pipech/erpnext-docker-debian:version-15-latest 27 | 28 | docker run -d -p 8000:8000 -p 9000:9000 -p 3306:3306 pipech/erpnext-docker-debian:version-15-latest 29 | ``` 30 | 31 | The site should be available at http://localhost:8000 after 1-2 minutes. 32 | 33 | ### Development Setup 34 | 35 | This is a self-contained development setup. Developers can fully isolate their environment. The setup utilizes Visual Studio Code and its Dev Containers feature. 36 | 37 | 1. Open Visual Studio Code. 38 | 1. Open the Command Palette (View > Command Palette or Ctrl + Shift + P). 39 | 1. Type: `Open Folder in Container`. 40 | 1. Select the `setup_development` folder. 41 | 42 | For every startup, run: 43 | 44 | ```sh 45 | sudo service mariadb start 46 | bench start 47 | ``` 48 | 49 | ### Production Setup 50 | 51 | For best practices in a production environment, [Official Frappe Docker](https://github.com/frappe/frappe_docker). 52 | 53 | ## User & Password 54 | 55 | ``` 56 | ERPNext | U: administrator P: 12345 57 | MariaDB | U: root P: 12345 58 | ``` 59 | 60 | ## Build Process 61 | 62 | For detailed information on the build process, please review the [`Dockerfile`](./Dockerfile) and [`.github/workflows/push-docker.yml`](./.github/workflows/push-docker.yml). 63 | 64 | In summary: 65 | 66 | - Starts with the `frappe/bench` image as the base. 67 | - Integrates all necessary production dependencies like MariaDB, Redis, etc. 68 | - Sets up new sites and verifies their functionality by checking for a response code of 200. 69 | - Upon successful verification, tags and pushes the images to [Docker hub](https://hub.docker.com/r/pipech/erpnext-docker-debian/). 70 | 71 | Images will be automatically generated every Sunday at 00:00. 72 | 73 | ## Tag version semantic 74 | 75 | **`15-F45.0_E39.0`** represents: 76 | 77 | - Frappe version 15.45.0 78 | - ERPNext version 15.39.0 79 | 80 | ## Contributing 81 | 82 | We welcome pull requests for new features, bug fixes, enhancements to documentation (including typo corrections), and suggestions. Your contributions help improve the project! 83 | 84 | ## License 85 | 86 | This project is licensed under the MIT License. 87 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM frappe/bench:v5.19.0 2 | # https://github.com/frappe/frappe_docker/blob/947fbae8bb051ec85ec2a817b2fa3fe2d9eea779/images/bench/Dockerfile 3 | # frappe/bench use debian:bookworm-slim as a base image 4 | # then it install dependencies for bench (python, nodejs) 5 | 6 | ############################################### 7 | # ARG 8 | ############################################### 9 | ARG adminPass=12345 10 | ARG mysqlPass=12345 11 | ARG pythonVersion=python3 12 | ARG appBranch=version-15 13 | 14 | ############################################### 15 | # ENV 16 | ############################################### 17 | ENV \ 18 | # [Note] frappe user has been set from frappe/bench image 19 | systemUser=frappe \ 20 | # Dependencies version 21 | # [Note] Frappe only support up to mariadb 10.8 (as of 2023-Nov) 22 | # but 10.8 isn't lts version so I use 10.6 instead 23 | mariadbVersion=10.6 \ 24 | # Frappe Related 25 | benchPath=bench-repo \ 26 | benchFolderName=bench \ 27 | benchRepo="https://github.com/frappe/bench" \ 28 | # [Note] Some how bench use v5.x as Master and Master didn't get the updates 29 | # https://github.com/frappe/bench/pull/1270 30 | benchBranch=v5.x \ 31 | frappeRepo="https://github.com/frappe/frappe" \ 32 | erpnextRepo="https://github.com/frappe/erpnext" \ 33 | siteName=site1.local 34 | 35 | ############################################### 36 | # Config File 37 | ############################################### 38 | # MariaDB config 39 | COPY ./mariadb.cnf /home/$systemUser/mariadb.cnf 40 | # image entrypoint 41 | COPY --chown=1000:1000 ./entrypoint.sh /usr/local/bin/entrypoint.sh 42 | 43 | # set entrypoint permission 44 | ## prevent: docker Error response from daemon OCI runtime create failed starting container process caused "permission denied" unknown 45 | RUN sudo chmod +x /usr/local/bin/entrypoint.sh 46 | 47 | ############################################### 48 | # Install Dependencies 49 | ############################################### 50 | RUN sudo apt-get update \ 51 | && sudo apt-get install -y -q \ 52 | # [fix] "debconf: delaying package configuration, since apt-utils is not installed" 53 | apt-utils \ 54 | # [fix] "debconf: unable to initialize frontend: Dialog" 55 | # https://github.com/moby/moby/issues/27988 56 | && echo "debconf debconf/frontend select Noninteractive" | sudo debconf-set-selections \ 57 | ############################################### 58 | # Install dependencies: MariaDB 59 | ############################################### 60 | && sudo apt-get install -y -q apt-transport-https curl \ 61 | && sudo mkdir -p /etc/apt/keyrings \ 62 | && sudo curl -o /etc/apt/keyrings/mariadb-keyring.pgp "https://mariadb.org/mariadb_release_signing_key.pgp" \ 63 | && echo "deb [signed-by=/etc/apt/keyrings/mariadb-keyring.pgp] https://mirror.kku.ac.th/mariadb/repo/10.11/debian bookworm main" | sudo tee /etc/apt/sources.list.d/mariadb.list \ 64 | && sudo apt-get update \ 65 | && sudo apt-get install -y -q \ 66 | mariadb-server \ 67 | mariadb-client \ 68 | mariadb-common \ 69 | libmariadb3 \ 70 | python3-mysqldb \ 71 | ############################################### 72 | # Install dependencies: Redis 73 | ############################################### 74 | && sudo apt-get install -y -q \ 75 | redis-server \ 76 | ############################################### 77 | # Install dependencies: supervisor 78 | ############################################### 79 | && sudo apt-get install -y -q \ 80 | supervisor \ 81 | ############################################### 82 | # Install dependencies: nginx 83 | ############################################### 84 | && sudo apt-get install -y -q \ 85 | nginx \ 86 | ############################################### 87 | # clean-up 88 | ############################################### 89 | && sudo apt-get autoremove --purge -y \ 90 | && sudo apt-get clean -y \ 91 | ############################################### 92 | # Init Bench & Setup Site 93 | ############################################### 94 | # copy MariaDB Config 95 | && sudo cp /home/$systemUser/mariadb.cnf /etc/mysql/mariadb.cnf \ 96 | && sudo service mariadb start \ 97 | && sudo mariadb --user="root" --password="${mysqlPass}" --execute="ALTER USER 'root'@'localhost' IDENTIFIED BY '${mysqlPass}';" \ 98 | ############################################### 99 | # Init Bench 100 | ############################################### 101 | && bench init $benchFolderName --frappe-path $frappeRepo --frappe-branch $appBranch --python $pythonVersion \ 102 | # cd into bench folder 103 | && cd $benchFolderName \ 104 | # install erpnext 105 | && bench get-app erpnext $erpnextRepo --branch $appBranch \ 106 | # delete temp file 107 | && sudo rm -rf /tmp/* \ 108 | # start new site 109 | && bench new-site $siteName \ 110 | --mariadb-root-password $mysqlPass \ 111 | --admin-password $adminPass \ 112 | && bench --site $siteName install-app erpnext \ 113 | # use site 114 | && bench use $siteName \ 115 | # compile all python file 116 | ## the reason for not using python3 -m compileall -q /home/$systemUser/$benchFolderName/apps 117 | ## is to ignore frappe/node_modules folder since it will cause syntax error 118 | && $pythonVersion -m compileall -q /home/$systemUser/$benchFolderName/apps/frappe/frappe \ 119 | && $pythonVersion -m compileall -q /home/$systemUser/$benchFolderName/apps/erpnext/erpnext 120 | 121 | ############################################### 122 | # WORKDIR 123 | ############################################### 124 | WORKDIR /home/$systemUser/$benchFolderName 125 | 126 | ############################################### 127 | # FINALIZED 128 | ############################################### 129 | # image entrypoint script 130 | CMD ["/usr/local/bin/entrypoint.sh"] 131 | 132 | # expose port 133 | EXPOSE 8000 9000 3306 134 | --------------------------------------------------------------------------------