How to Update Docker Single Compose Setup Including Custom Production Image?

Hi :slightly_smiling_face:

Fairly new to Frappe/ERPNext, but very happy to have found it and like it very much!

Cloned the frappe/frappe_docker Git repo and followed the instructions in the docs/ and docs/ guides adapting the pwd.yml to our needs. Also created a custom frappe/erpnext docker image based on v15.6.1 using a customized images/production/Containerfile. In addition installed the payments and webshop apps in the single default site. For maintaining our ERPNext customizations during upgrades created and installed a custom app. All this works nicely but cannot seem to find guidance on how to update this setup.

How would I go about updating all components? A simple bench update returns an error and it appears it is also not supposed to work in a production setup.

Thanks much for any help!

Thanks a lot, Revant, for your quick and helpful reply! Found out about updating everything and also stumbled over the assets issue. Regarding the same, wouldn’t a simple bench build right after the bench migrate also solve the problem? It also updates the assets.json and assets-rtl.json files which seem to be the only files causing the troubles. When deleting and rebuilding the assets volume as @khoran suggests one may lose files in the (by default empty) js and css subfolders.

Revant, I’d appreciate your comments on my handling updates to the custom app. It’s not using a Git remote repo approach, but introducing a custom-apps folder besides the apps folder on its own docker volume with a symlink from custom-apps to apps.

Customize frappe_docker/images/production/Containerfile into frappe_docker/images/my/Containerfile:

COPY resources/nginx-template.conf /templates/nginx/frappe.conf.template
COPY resources/ /usr/local/bin/

COPY images/my/custom-app.cfg /home/frappe   # new
RUN chown frappe:frappe /home/frappe/custom-app.cfg   # new

FROM base AS builder
RUN bench init \
  --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 && \
  bench get-app --branch=${ERPNEXT_BRANCH} --resolve-deps erpnext ${ERPNEXT_REPO} && \
  cat /home/frappe/custom-app.cfg | bench new-app custom_app && rm /home/frappe/custom-app.cfg && \ # new
  test ! -d custom-apps && mkdir custom-apps && chown frappe:frappe custom-apps && \ # new
  mv apps/custom_app custom-apps && \ # new
  ln -s /home/frappe/frappe-bench/custom-apps/custom_app apps && \ # new
  echo "{}" > sites/common_site_config.json && \
  find apps -mindepth 1 -path "*/.git" ! -path "apps/custom_app/*" | xargs rm -rf # modified

FROM base as erpnext
  "/home/frappe/frappe-bench/sites", \
  "/home/frappe/frappe-bench/sites/assets", \
  "/home/frappe/frappe-bench/logs", \ # modified
  "/home/frappe/frappe-bench/custom-apps" \ # new

Contents of images/my/custom-app.cfg (responses to bench new-app):

Custom App
ERPNext Customizations
Example Company

Customize pwd.yml into my.yml:

    # replace each "image: frappe/erpnext" with "image: my/erpnext"
    image: my/erpnext:v15.10.0 # modified
    # add "custom-apps" to each "volumes" section containing the "sites" volume
      - sites:/home/frappe/frappe-bench/sites
      - logs:/home/frappe/frappe-bench/logs
      - custom-apps:/home/frappe/frappe-bench/custom-apps # new
      - >
        bench new-site --no-mariadb-socket --admin-password=admin \
          --db-root-password=admin --install-app erpnext --install-app custom_app \
          --set-default frontend; # modified
  custom-apps: # new

A complete update would then involve the following steps:

user@pc:~/projects/frappe_docker$ # run a complete backup
user@pc:~/projects/frappe_docker$ docker compose -f my.yml stop
user@pc:~/projects/frappe_docker$ docker compose -f my.yml rm # remove containers
user@pc:~/projects/frappe_docker$ git pull
user@pc:~/projects/frappe_docker$ diff images/production/Containerfile images/my/Containerfile
user@pc:~/projects/frappe_docker$ # update images/my/Containerfile
user@pc:~/projects/frappe_docker$ diff pwd.yml my.yml
user@pc:~/projects/frappe_docker$ # update my.yml
user@pc:~/projects/frappe_docker$ # "v15.10.0" is new version in my.yml
user@pc:~/projects/frappe_docker$ docker build -f images/my/Containerfile -t my/erpnext:v15.10.0 --no-cache .
user@pc:~/projects/frappe_docker$ docker compose -f my.yml up
user@pc:~/projects/frappe_docker$ docker exec -it frappe_docker-frontend-1 /bin/bash
frappe@frontend:~/frappe-bench$ bench migrate
frappe@frontend:~/frappe-bench$ # update sites/assets/assets*.json
frappe@frontend:~/frappe-bench$ bench build

Bench build doesn’t work in production containers

Read Frequently Asked Questions · frappe/frappe_docker Wiki · GitHub

Hope someone helps you create image with new app instead of standard way of using existing apps

Thanks much, Revant, for your quick and helpful reply. Yes, understand now that it’s an improper mix of production and development setup.

By the help of your links got a proper development environment created and also a proper production deployment. A custom app is transferring any changes via private Git repo from development to production using fixtures.

To solve the assets issue added to the custom Containerfile:

RUN export APP_INSTALL_ARGS="" && \
  cd /home/frappe/frappe-bench && \
  mkdir -p sites-orig/assets && chown -R frappe:frappe sites-orig && \   # new
  cp -p sites/assets/assets*.json sites-orig/assets && \   # new
  echo "{}" > sites/common_site_config.json && \

Then, after an update one needs to copy the assets*.json files back to the sites volume:

docker exec -it frappe_docker-frontend-1 \
  /bin/bash -c "cp -p sites-orig/assets/assets*.json sites/assets"

Thanks again!

I have setup a whole CI/CD pipeline using frappe-docker and custom apps. I will request with the company if we can open source it.

In a nutshell, every time you make a merge to main branch for custom app, frappe docker triggers a pipeline that builds Docker image and deploys it in the server.