Best way to update docker images

Hello there.

I got stuck with the bug described on Custom Print Format Gives weasyprint libcairo2 error (on Docker Installation) while printing a custom sales invoice, and it seems that the issue has been solved in the following fix.

Error description:

OSError: no library called "cairo" was found
no library called "libcairo-2" was found
cannot load library 'libcairo.so.2': libcairo.so.2: cannot open shared object file: No such file or directory
cannot load library 'libcairo.2.dylib': libcairo.2.dylib: cannot open shared object file: No such file or directory
cannot load library 'libcairo-2.dll': libcairo-2.dll: cannot open shared object file: No such file or directory

What is the best way to update the images I am using on my environment? I followed the single server example
Wierd thing is that I already rebuild some of the images with the latest version available on dockehub, but it did not solve the problem, what makes me think that the fix on github did not went to upstream yet:

frappe@5eac7ced2070:~/frappe-bench/sites$ bench version
erpnext 14.12.1
frappe 14.22.0
payments 0.0.1

Thanks in advance for your time.

@Fillipe_Feitosa Have you found a fix for this issue? Experiencing the same.

libcairo2 was not added

these dependencies were added

can you list the packages needed?

Hello there, and sorry for the late response. I was travelling.

I partially solved this by building the following Dockerfile:

FROM frappe/erpnext-worker:v14.12.1

USER root

RUN mkdir -p /var/lib/apt/lists/partial && chmod 777 /var/lib/apt/lists/partial

RUN apt-get update && \
    apt-get install -y \
    libcairo2-dev \
    libpango1.0-0 \
    libpangocairo-1.0-0 \
    libgdk-pixbuf2.0-0

USER frappe


If you need help to build and use it let me know.

I said partially because I can create the PDF now, but I cannot print it. And I really need this invoice being printed.
The print button gives me the following error:

Traceback (most recent call last):
  File "apps/frappe/frappe/website/serve.py", line 18, in get_response
    response = renderer_instance.render()
  File "apps/frappe/frappe/website/page_renderers/template_page.py", line 78, in render
    html = self.get_html()
  File "apps/frappe/frappe/website/utils.py", line 510, in cache_html_decorator
    html = func(*args, **kwargs)
  File "apps/frappe/frappe/website/page_renderers/template_page.py", line 89, in get_html
    self.update_context()
  File "apps/frappe/frappe/website/page_renderers/template_page.py", line 157, in update_context
    data = self.run_pymodule_method("get_context")
  File "apps/frappe/frappe/website/page_renderers/template_page.py", line 219, in run_pymodule_method
    return method(self.context)
  File "apps/frappe/frappe/www/printview.py", line 51, in get_context
    body = get_rendered_template(
  File "apps/frappe/frappe/www/printview.py", line 153, in get_rendered_template
    format_data_map[df.get("fieldname")] = df
AttributeError: 'str' object has no attribute 'get'

Hey @revant_one , thanks for sharing. I am following this issue,

But even with that, it turns out that it still no possible to print the custom print format, even though the pdf is being generated.

Any ideas? The github discussion tells us to do commit the new builder from the app until it is solved.

And by the way: I followed the guide on FAQ, but I still had to manually place the required libraries on a new Dockerfile. Am I missing something?

F.F.

This is old image.

Use single image now. frappe_docker/migrate-from-multi-image-setup.md at main · frappe/frappe_docker · GitHub

Also Docker hub images may be deleted after they make changes to their account policies for repo owners.

follow container registry migration proposal · Issue #1103 · frappe/frappe_docker · GitHub

New way of building custom image doesn’t depend on erpnext image

1 Like

Thanks for your answer.

I tried to change all the docker images as you suggested and followed your docs. My yaml docker compose file looks like this after direct migration.


name: erpnext-one
services:
  backend:
    depends_on:
      configurator:
        condition: service_completed_successfully
    image: frappe/erpnext:v14.19.0
    container_name: backend
    networks:
      bench-network: null
      mariadb-network: null
    volumes:
    - type: volume
      source: sites
      target: /home/frappe/frappe-bench/sites
      volume: {}
    - type: volume
      source: assets
      target: /home/frappe/frappe-bench/sites/assets
      read_only: true
      volume: {}
  configurator:
    depends_on:
      redis:
        condition: service_started
    environment:
      DB_HOST: mariadb-database
      DB_PORT: "3306"
      REDIS_CACHE: redis:6379/0
      REDIS_QUEUE: redis:6379/1
      REDIS_SOCKETIO: redis:6379/2
      SOCKETIO_PORT: "9000"
    image: frappe/erpnext:v14.19.0
    restart: "no"
    entrypoint:
      - bash
      - -c
    command:
      - >
        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_SOCKETIO";
        bench set-config -gp socketio_port $$SOCKETIO_PORT;
    networks:
      bench-network: null
      mariadb-network: null
    volumes:
    - type: volume
      source: sites
      target: /home/frappe/frappe-bench/sites
      volume: {}
  frontend:
    depends_on:
      backend:
        condition: service_started
      websocket:
        condition: service_started
    environment:
      BACKEND: backend:8000
      FRAPPE_SITE_NAME_HEADER: $$host
      SOCKETIO: websocket:9000
      UPSTREAM_REAL_IP_ADDRESS: 127.0.0.1
      UPSTREAM_REAL_IP_HEADER: X-Forwarded-For
      UPSTREAM_REAL_IP_RECURSIVE: "off"
    image: frappe/erpnext:v14.19.0
    command:
      - nginx-entrypoint.sh
    environment:
      BACKEND: backend:8000
      SOCKETIO: websocket:9000
    labels:
      traefik.docker.network: traefik-public
      traefik.enable: "true"
      traefik.http.routers.erpnext-one-http.entrypoints: http
      traefik.http.routers.erpnext-one-http.middlewares: https-redirect
      traefik.http.routers.erpnext-one-http.rule: Host(`me.bizsuite.pt`,`petvet.bizsuite.pt`)
      traefik.http.routers.erpnext-one-http.service: erpnext-one
      traefik.http.routers.erpnext-one-https.entrypoints: https
      traefik.http.routers.erpnext-one-https.rule: Host(`me.bizsuite.pt`,`petvet.bizsuite.pt`)
      traefik.http.routers.erpnext-one-https.service: erpnext-one
      traefik.http.routers.erpnext-one-https.tls: "true"
      traefik.http.routers.erpnext-one-https.tls.certresolver: le
      traefik.http.services.erpnext-one.loadbalancer.server.port: "8080"
    networks:
      bench-network: null
      traefik-public: null
    volumes:
    - type: volume
      source: sites
      target: /home/frappe/frappe-bench/sites
      volume: {}
  queue-default:
    command:
    - bench
    - worker
    - --queue
    - default
    depends_on:
      configurator:
        condition: service_completed_successfully
    image: frappe/erpnext:v14.19.0
    networks:
      bench-network: null
      mariadb-network: null
    volumes:
    - type: volume
      source: sites
      target: /home/frappe/frappe-bench/sites
      volume: {}
  queue-long:
    command:
    - bench
    - worker
    - --queue
    - long
    depends_on:
      configurator:
        condition: service_completed_successfully
    image: frappe/erpnext:v14.19.0
    networks:
      bench-network: null
      mariadb-network: null
    volumes:
    - type: volume
      source: sites
      target: /home/frappe/frappe-bench/sites
      volume: {}
  queue-short:
    command:
    - bench
    - worker
    - --queue
    - short
    depends_on:
      configurator:
        condition: service_completed_successfully
    image: frappe/erpnext:v14.19.0
    networks:
      bench-network: null
    volumes:
    - type: volume
      source: sites
      target: /home/frappe/frappe-bench/sites
      volume: {}
  redis:
    image: redis:6.2-alpine
    networks:
      bench-network: null
      mariadb-network: null
    volumes:
    - type: volume
      source: redis-data
      target: /data
      volume: {}
  scheduler:
    command:
    - bench
    - schedule
    depends_on:
      configurator:
        condition: service_completed_successfully
    image: frappe/erpnext:v14.19.0
    networks:
      bench-network: null
      mariadb-network: null
    volumes:
    - type: volume
      source: sites
      target: /home/frappe/frappe-bench/sites
      volume: {}
  websocket:
    depends_on:
      configurator:
        condition: service_completed_successfully
    image: frappe/erpnext:v14.19.0
    command:
      - node
      - /home/frappe/frappe-bench/apps/frappe/socketio.js
    networks:
      bench-network: null
      mariadb-network: null
    volumes:
    - type: volume
      source: sites
      target: /home/frappe/frappe-bench/sites
      volume: {}
networks:
  bench-network:
    name: erpnext-one
  mariadb-network:
    name: mariadb-network
    external: true
  traefik-public:
    name: traefik-public
    external: true
volumes:
  assets:
    name: erpnext-one_assets
  redis-data:
    name: erpnext-one_redis-data
  sites:
    name: erpnext-one_sites
x-backend-defaults:
  depends_on:
    configurator:
      condition: service_completed_successfully
  image: frappe/erpnext:v14.19.0
  volumes:
  - sites:/home/frappe/frappe-bench/sites
x-depends-on-configurator:
  depends_on:
    configurator:
      condition: service_completed_successfully
x-erpnext-backend-image:
  image: frappe/erpnext:v14.19.0

I rebuild all the conttainers using the following command:

docker compose -f erpnext-one.yaml up -d --build --force-recreate

But I get ** Internal Server Error **. Backend logs shows that I am missing payments module, but this wierd because my website was already running.

Traceback (most recent call last):
  File "/home/frappe/frappe-bench/env/lib/python3.10/site-packages/gunicorn/workers/gthread.py", line 271, in handle
    keepalive = self.handle_request(req, conn)
  File "/home/frappe/frappe-bench/env/lib/python3.10/site-packages/gunicorn/workers/gthread.py", line 323, in handle_request
    respiter = self.wsgi(environ, resp.start_response)
  File "/home/frappe/frappe-bench/env/lib/python3.10/site-packages/werkzeug/local.py", line 237, in application
    return ClosingIterator(app(environ, start_response), self.cleanup)
  File "/home/frappe/frappe-bench/env/lib/python3.10/site-packages/werkzeug/wrappers/request.py", line 194, in application
    resp = f(*args[:-2] + (request,))
  File "/home/frappe/frappe-bench/apps/frappe/frappe/app.py", line 84, in application
    response = handle_exception(e)
  File "/home/frappe/frappe-bench/apps/frappe/frappe/app.py", line 324, in handle_exception
    response = get_response("message", http_status_code=http_status_code)
  File "/home/frappe/frappe-bench/apps/frappe/frappe/website/serve.py", line 27, in get_response
    response = ErrorPage(exception=e).render()
  File "/home/frappe/frappe-bench/apps/frappe/frappe/website/page_renderers/error_page.py", line 7, in __init__
    super().__init__(path=path, http_status_code=http_status_code)
  File "/home/frappe/frappe-bench/apps/frappe/frappe/website/page_renderers/template_page.py", line 45, in __init__
    self.set_template_path()
  File "/home/frappe/frappe-bench/apps/frappe/frappe/website/page_renderers/template_page.py", line 54, in set_template_path
    app_path = frappe.get_app_path(app)
  File "/home/frappe/frappe-bench/apps/frappe/frappe/__init__.py", line 1356, in get_app_path
    return get_pymodule_path(app_name, *joins)
  File "/home/frappe/frappe-bench/apps/frappe/frappe/__init__.py", line 1373, in get_pymodule_path
    return os.path.join(os.path.dirname(get_module(scrub(modulename)).__file__ or ""), *joins)
  File "/home/frappe/frappe-bench/apps/frappe/frappe/__init__.py", line 1327, in get_module
    return importlib.import_module(modulename)
  File "/usr/local/lib/python3.10/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1050, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1027, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1004, in _find_and_load_unlocked
ModuleNotFoundError: No module named 'payments'

I also tried to to install the payments module, but bench returns me that I already have the folder:

frappe@e8867c4980aa:~/frappe-bench$ bench get-app payments
A directory for the application 'payments' already exists. Do you want to continue and overwrite it? [y/N]:

Would you have some insights? I am tinkering with this, the only thing I made different from the tutorial was the sites:/home/frappe/frappe-bench/sites, but this is on Frontend. and if I try to force the payments installation, I get a permission error:

PermissionError: [Errno 13] Permission denied: './sites/apps.txt'

Read this: Frequently Asked Questions · frappe/frappe_docker Wiki · GitHub

In case of production setup you need to build your custom image for installing apps. This repository only publishes frappe/erpnext image with no additional app. You CANNOT bench get-app in running containers.

Isn´t it weird that I need to install the payments module, even though my environment was working before, and the directory for the application already exists?

A directory for the application 'payments' already exists. Do you want to continue and overwrite it? [y/N]:

Sorry to insist on this. The directions are not totally clear. It is not clear to me that I need to build a custom image to install the payments, or what changed in the infrastructure that implies that I need to do that.

Once again, thanks for your time and atention.

payments is now optional dependency.

newer images will not have payments, old ones had it.

ls -1 apps > sites/apps.txt will remove it from apps.txt

you’ll need to uninstall app from site, or bulid custom image with it.