Yet another wkhtmltopdf thread

I can confirm that printing works when creating a new Print Format using the new builder. Does this consequently mean that all format will need to be recreated?

EDIT:
I just tried sending an invoice via PDF using the new format but receive this error now:

AttributeError: ‘str’ object has no attribute ‘get’

EDIT2:
What is confusing to me is that I used a PWD instance where printing works, running the PWD yaml file leads to the same errors as a supposed production installation.

EDIT3:
If I enter host_name as https://erp.domain.com then I get a timeout error.

It would be fantastic if we could resolve this.

@revant_one @Sch-Tim @tmatteson

UPDATE:

For anyone struggling with this, for now I got it working like this with the following setup:

  1. Nginx Proxy Manager for TLS termination
  2. ExposePort 8080 on nginx service in Docker Compose file
  3. set “host_name”: “http://10.10.0.111:8080” in site specific config file

I am running Proxmox VE and ERPNext on a VM with Nginx Proxy Manager routing traffic via internal IPs. 10.10.0.111 is the VM IP address. If we could somehow manage to make sure that ERPNext runs behind a separate Nginx Reverse Proxy without people stumbling accross these things I think a lot of people would benefit.

3 Likes

Glad you got it working through this workaround. Sounds to me like a DNS problem though. The wkhtmltopdf binary needs to be able to resolve the site. If it can’t, then you have to fetch the site by IP like you’ve done.

Based on what you shared so far, I’d guess that internal DNS resolution for your site isn’t working for your docker containers. You could determine if that’s the case using a nicolaka/netshoot container connected to your erpnext network. I’d also check to make sure docker is configured to use a DNS server that has an authoritative zone that can give a valid response to DNS A queries for your erpnext site.

Holy shit now it works here too!

One thought of mine:
Setting a static IP is probably not a good idea in some settings.
If you use docker compose or portainer with stacks the IPs might change after rebuild.

Expose port 8080 on the “frontend” service and set host_name to http://frontend:8080
That way you’re future proof to any IP change.

I should add that I always thought that host_name had to point to the backend service and not the frontend, so I had no chance. But exposing the port was needed here too.

Thanks for the nice collaborative problem solving.

For everyone struggling with this. Here are my compose.yml and env files:
compose.yml
env

Please change the env variables as needed.

1 Like

@danielslyman
I just realized that there is (at least) one caveat with that solution:
When I add things like images to an email, they aren’t shown properly because they are loaded from frontend:8080. Obviously that won’t work.
Probably any other links won’t work too, so that’s a bummer.
Did you find a solution for that?

1 Like

Hey there,

so for some reason adding https://erp.domain.com into the site specific config file works for me. I removed the DNS entry in /etc/hosts which was seemingly causing the issue. Have you had any progress?

I finally made it. I had to make a small change in the docker build files and a few in the compose file.

I forked the frappe_docker git and implemented my changes.
You can find it here: GitHub - Sch-Tim/frappe_docker: Docker images for production and development setups of the Frappe framework and ERPNext

Change:
I updated the nginx resources from the frappe-docker git:
resources/nginx-template.conf

line 10: listen ${NGINX_PORT};
line 11: server_name ${NGINX_SERVER_NAME};

resources/nginx-entrypoint.sh
add

if [[ -z “$NGINX_SERVER_NAME” ]]; then
echo ‘NGINX_SERVER_NAME defaulting to $host’
export NGINX_SERVER_NAME=‘$host’
fi

if [[ -z “$NGINX_PORT” ]]; then
echo ‘NGINX_PORT defaulting to 8080’
export NGINX_PORT=8080
fi

after the other if blocks
and

${TRAEFIK_DOMAIN}
${NGINX_PORT}

into the envsubst string

reason:
I had the problem that nginx produced gateway errors when I redirected erp.domain traffic directly into the frontend container. I tracked the error down to the server_name part in the config. It equals the site name. In my case, it was “frontend”. To uncouple the site name from the domain, I added a second variable.
Additionally, I had to change the listening port to 80 because the default http port is 80. Otherwise, I had to set host_name to erp.domain:8080.
By adding an NGINX_PORT variable, I kept it dynamic.

Change:
Add

hostname: ${TRAEFIK_DOMAIN}

to the frontend service in the compose file
Reason:
One often heard tip is that you should add the frontend container ip to the hosts file.
While this works, it causes two problems:
The first one is that you need root permissions to alter that file.
The second one is that the container IP may change after rebuild.
I realized that docker seems to resolve hostnames internally first. If there is a container with the hostname erp.domain, it goes that way instead of asking the DNS.

Change:
Add

bench set-config -g host_name “http://$TRAEFIK_DOMAIN”;
bench --site frontend set-config host_name “http://$TRAEFIK_DOMAIN”;

as additional config commands to the configurator service in the compose file.

Reason:
The reason should be obvious. You want the backend to explicitly call the internal frontend container. I don’t know what host_name is the default if you don’t set it. But it seems that you need to set it. I chose to set it globally and sitewise to be extra sure.

3 Likes

@revant_one would it make sense to integrate above changes into the official frappe_docker repo?

Sure, send a PR and make sure tests pass, it’s backward compatible with everything including helm charts, or specify migration guide.

We can completely override the nginx template file by mounting a custom one. No need to do any change to frappe_docker in that case.

Someone else did this trick and used $updated_host instead of $host in custom nginx conf template.

map $host $updated_host {
    default $host;
    "sitehost" erp.example.com;
}

Further you can even change /etc/hosts using extra_hosts for docker or equivalent configuration for other orchestrators.

1 Like

Changing frappe_docker directly by including those changes would make it possible to use everything out of the box. You only have to add the specific environment variables.

I know about the extra_hosts parameter but I had the problem that I don’t know what IP the container will get after rebuilding the stack. Do you know how to fetch it instead of hardcoding it?

1 Like

Make sure images remain backward compatible for different setups. OR else people will upgrade and need some additional tweaks to get new image working.

May be an entrypoint script? Something like this?

Hi @danielslyman ,
If set “host_name”: “http://10.10.0.111:8080” in site specific config file then PDF print will working.
But we will facing another issue of Email Notitifcation,coz site_url in Email template will be get from host_name.
So I think set host_name is a IP will not solve a root cause

is this solution work for multi-sites ? How can we define TRAEFIK_DOMAIN align with each site ?

DNS ----> Try running “curl -vI http://web.erp.domain”. Once the domain responds, wkhtmltopdf will work without any issues.

Of course, you need to make sure it’s accessible.

In my case, using a proxy, I edited the /etc/hosts file and, instead of using the local server’s IP, I used the proxy’s IP. That’s it—no other changes needed.

Hi folks,

I’m experiencing a similar issue, if not the same one, as discussed below: Help Needed: Error with PDF Generation when Sending Email.

I’m unable to send emails using my Gmail account, and I also can’t print invoices. I have already set the host_name to my site name, which is proxied through Nginx Proxy Manager, but the issue persists.

Has anyone found a solution or have any suggestions on what else I might try? Thanks in advance for your help!

Confirm if the site location is accessible from within workers and gunicorn containers. If the process cannot connect to host_name it’ll not be able to generate pdf.

Access from browser is different, that you already have.

Use extra_hosts or figure out way to resolve images from containers.

Thank you for your prompt response! I would appreciate your assistance, and I’ll gladly treat you to lunch for your help. :sweat_smile:

I’m currently running Docker with WSL, Portainer, Nginx Proxy Manager, and a Cloudflare tunnel in a container. The frontend is proxied through Nginx and is accessible via the internet through the Cloudflare tunnel. All containers have external site access, so I don’t need to add extra_hosts directives (which I believe have been deprecated). They are utilizing the Docker bridge network. For example, I’m able to successfully run:

curl https://test.menteware.com

Here’s the stack configuration I have set up in Portainer:

version: "3"

services:
  backend:
    image: edardev/erpnext:v15.38.1-print
    deploy:
      restart_policy:
        condition: on-failure
    volumes:
      - sites:/home/frappe/frappe-bench/sites
      - logs:/home/frappe/frappe-bench/logs

  configurator:
    image: edardev/erpnext:v15.38.1-print
    deploy:
      restart_policy:
        condition: none
    entrypoint:
      - bash
      - -c
    # add redis_socketio for backward compatibility
    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: edardev/erpnext:v15.38.1-print
    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 --no-mariadb-socket --admin-password="${ADMIN_PWD}" --db-root-password="${DB_ROOT_PWD}" --install-app erpnext --set-default "${SITE_NAME}";
        bench set-config -g host_name "https://${SITE_NAME}";
        bench --site "${SITE_NAME}" set-config host_name "https://${SITE_NAME}";
        bench --site "${SITE_NAME}" install-app payments;
        bench --site "${SITE_NAME}" install-app hrms;
        bench --site "${SITE_NAME}" install-app ecommerce_integrations;
        bench --site "${SITE_NAME}" install-app webshop;
        bench --site "${SITE_NAME}" install-app print_designer;
        # bench --site "${SITE_NAME}" migrate
        # bench clear-cache
        # bench build

        
      

  db:
    image: mariadb:10.6
    healthcheck:
      test: mysqladmin ping -h localhost --password="${DB_ROOT_PWD}"
      interval: 1s
      retries: 15
    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: "${DB_ROOT_PWD}"
    volumes:
      - db-data:/var/lib/mysql

  frontend:
    image: edardev/erpnext:v15.38.1-print
    depends_on:
      - websocket
    deploy:
      restart_policy:
        condition: on-failure
    command:
      - nginx-entrypoint.sh
    environment:
      BACKEND: backend:8000
      FRAPPE_SITE_NAME_HEADER: "${SITE_NAME}"
      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:
      - "${FRONTEND_PORT:-9999}:8080"

  queue-long:
    image: edardev/erpnext:v15.38.1-print
    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

  queue-short:
    image: edardev/erpnext:v15.38.1-print
    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

  redis-queue:
    image: redis:6.2-alpine
    deploy:
      restart_policy:
        condition: on-failure
    volumes:
      - redis-queue-data:/data

  redis-cache:
    image: redis:6.2-alpine
    deploy:
      restart_policy:
        condition: on-failure
    volumes:
      - redis-cache-data:/data

  scheduler:
    image: edardev/erpnext:v15.38.1-print
    deploy:
      restart_policy:
        condition: on-failure
    command:
      - bench
      - schedule
    volumes:
      - sites:/home/frappe/frappe-bench/sites
      - logs:/home/frappe/frappe-bench/logs

  websocket:
    image: edardev/erpnext:v15.38.1-print
    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

volumes:
  db-data:
  redis-queue-data:
  redis-cache-data:
  sites:
  logs:


And this is error from Frappe when sending the queue email with the document attached:

Traceback (most recent call last):
  File "apps/frappe/frappe/utils/pdf.py", line 90, in get_pdf
    filedata = pdfkit.from_string(html, options=options or {}, verbose=True)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "env/lib/python3.11/site-packages/pdfkit/api.py", line 75, in from_string
    return r.to_pdf(output_path)
           ^^^^^^^^^^^^^^^^^^^^^
  File "env/lib/python3.11/site-packages/pdfkit/pdfkit.py", line 201, in to_pdf
    self.handle_error(exit_code, stderr)
  File "env/lib/python3.11/site-packages/pdfkit/pdfkit.py", line 155, in handle_error
    raise IOError('wkhtmltopdf reported an error:\n' + stderr)
OSError: wkhtmltopdf reported an error:
Exit with code 1 due to network error: ContentOperationNotPermittedError


During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "apps/frappe/frappe/email/doctype/email_queue/email_queue.py", line 167, in send
    message = ctx.build_message(recipient.recipient)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "apps/frappe/frappe/email/doctype/email_queue/email_queue.py", line 315, in build_message
    message = self.include_attachments(message)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "apps/frappe/frappe/email/doctype/email_queue/email_queue.py", line 392, in include_attachments
    print_format_file = frappe.attach_print(**attachment)
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "apps/frappe/frappe/__init__.py", line 2232, in attach_print
    else get_print(doctype, name, **kwargs)
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "apps/frappe/frappe/__init__.py", line 2191, in get_print
    return get_pdf(html, options=pdf_options, output=output) if as_pdf else html
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "apps/frappe/frappe/utils/pdf.py", line 98, in get_pdf
    frappe.throw(_("PDF generation failed because of broken image links"))
  File "apps/frappe/frappe/__init__.py", line 652, in throw
    msgprint(
  File "apps/frappe/frappe/__init__.py", line 617, in msgprint
    _raise_exception()
  File "apps/frappe/frappe/__init__.py", line 568, in _raise_exception
    raise exc
frappe.exceptions.ValidationError: PDF generation failed because of broken image links

Email-queue Message:

Content-Type: multipart/mixed; boundary="===============2207987403685055338=="
MIME-Version: 1.0
Message-Id: <172882808235.8.12430365175257265920@test.menteware.com>
X-Original-From: Edward A <edward@menteware.com>
Subject: Re: Grant Plastics Ltd. (#ACC-SINV-2024-00001)
From: Edward A <test@menteware.com>
To: test@menteware.com
Date: Sun, 13 Oct 2024 14:01:22 -0000
Reply-To: test@menteware.com
X-Frappe-Site: https://test.menteware.com

--===============2207987403685055338==
Content-Type: multipart/alternative; boundary="===============7815968053194883997=="
MIME-Version: 1.0

--===============7815968053194883997==
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: quoted-printable

otortorootr

 [View this in your browser](https://test.menteware.com/Sales
Invoice/ACC-SINV-2024-00001?format=3Dsi-demo&key=3Da933c8ce3ad49bcb7ac6169d=
b13aab4)


--===============7815968053194883997==
Content-Type: text/html; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: quoted-printable

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.=
w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns=3D"http://www.w3.org/1999/xhtml">

<head>
	<meta name=3D"viewport" content=3D"width=3Ddevice-width">
	<meta http-equiv=3D"Content-Type" content=3D"text/html; charset=3DUTF-8">
	<title>Re: Grant Plastics Ltd. (#ACC-SINV-2024-00001)</title>
<style type=3D"text/css">@media only screen and (max-width: 700px) {
    .body-table.with-container .body-content {
        padding: 30px 20px !important
        }
    .body-table.with-container .email-container {
        padding: 20px !important;
        width: auto !important
        }
    }
.email-footer-container > div:not(:last-child) {margin-bottom:5px !importan=
t}
.table > thead:first-child > tr:first-child > th {border-top:none !importan=
t}
.ql-editor li > .ql-ui:before {display:inline-block !important;margin-left:=
-1.5em !important;margin-right:0.3em !important;text-align:right !important=
;white-space:nowrap !important;width:1.2em !important}
.ql-editor li[data-list=3Dbullet] > .ql-ui:before {content:"=E2=80=A2" !imp=
ortant}
.ql-editor li[data-list=3Dchecked] > .ql-ui:before {content:"=E2=98=91" !im=
portant}
.ql-editor li[data-list=3Dunchecked] > .ql-ui:before {content:"=E2=98=90" !=
important}
.ql-editor li[data-list=3Dordered] > .ql-ui:before {content:counter(list-0,=
 decimal) ". " !important}
.ql-editor li[data-list=3Dordered].ql-indent-1 > .ql-ui:before {content:cou=
nter(list-1, lower-alpha) ". " !important}
.ql-editor li[data-list=3Dordered].ql-indent-2 > .ql-ui:before {content:cou=
nter(list-2, lower-roman) ". " !important}
.ql-editor li[data-list=3Dordered].ql-indent-3 > .ql-ui:before {content:cou=
nter(list-3, decimal) ". " !important}
.ql-editor .ql-indent-1:not(.ql-direction-rtl) {padding-left:3em !important}
.ql-editor li.ql-indent-1:not(.ql-direction-rtl) {padding-left:4.5em !impor=
tant}
.ql-editor .ql-indent-2:not(.ql-direction-rtl) {padding-left:6em !important}
.ql-editor li.ql-indent-2:not(.ql-direction-rtl) {padding-left:7.5em !impor=
tant}
.ql-editor .ql-indent-3:not(.ql-direction-rtl) {padding-left:9em !important}
.ql-editor li.ql-indent-3:not(.ql-direction-rtl) {padding-left:10.5em !impo=
rtant}</style>
</head>

<body style=3D"line-height:1.5; color:#1f272e">
	<table class=3D"body-table " cellpadding=3D"0" cellspacing=3D"0" style=3D"=
border-collapse:collapse; table-layout:fixed; margin:0 auto; border-spacing=
:0; font-weight:400; font-family:-apple-system, BlinkMacSystemFont, Segoe U=
I, Roboto, Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue=
, sans-serif; font-size:14px; line-height:1.4; height:100% !important; widt=
h:100% !important" height=3D"100% !important" width=3D"100% !important">
		<tr>
			<td class=3D"body-content" align=3D"center" valign=3D"top" style=3D"font=
-family:-apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu=
, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; padding:20p=
x 0">
				<table class=3D"email-container" border=3D"0" cellpadding=3D"0" cellspa=
cing=3D"0" width=3D" 100% ">
				=09
					<tr>
						<td valign=3D"top" style=3D"font-family:-apple-system, BlinkMacSystem=
Font, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, H=
elvetica Neue, sans-serif">
						=09
						</td>
					</tr>
					<tr>
						<td valign=3D"top" style=3D"font-family:-apple-system, BlinkMacSystem=
Font, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, H=
elvetica Neue, sans-serif">
							<table class=3D"email-body" border=3D"0" cellpadding=3D"0" cellspaci=
ng=3D"0" width=3D"100% !important" style=3D"font-size:14px; color:#1f272e; =
width:100% !important; min-width:100% !important">
								<tr>
									<td valign=3D"top" style=3D"font-family:-apple-system, BlinkMacSys=
temFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans=
, Helvetica Neue, sans-serif">
										<p style=3D"margin:1em 0 !important"></p>
<div class=3D"ql-editor read-mode" style=3D"white-space:normal"><p style=3D=
"margin:0 !important">otortorootr</p></div>
<p style=3D"margin:1em 0 !important">
	<a href=3D"https://test.menteware.com/Sales%20Invoice/ACC-SINV-2024-00001?=
format=3Dsi-demo&amp;key=3Da933c8ce3ad49bcb7ac6169db13aab4" style=3D"color:=
#2d95f0">View this in your browser</a>
</p>
									</td>
								</tr>
							</table>
						</td>
					</tr>
					<tr>
						<td valign=3D"top" style=3D"font-family:-apple-system, BlinkMacSystem=
Font, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, H=
elvetica Neue, sans-serif">
							<table class=3D"email-footer" border=3D"0" cellpadding=3D"0" cellspa=
cing=3D"0" width=3D"100% !important" style=3D"font-size:12px; line-height:2=
0px; width:100% !important; min-width:100% !important">
								<tr>
									<td valign=3D"top" data-email-footer=3D"true" style=3D"font-family=
:-apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, Canta=
rell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif">
										<div class=3D"email-footer-container text-muted" style=3D"margin-=
top:40px; color:#687178 !important">
	<!-- email_account_footer -->
=09

	<!-- sender_address -->
=09

	<!--unsubscribe link here-->

	<div class=3D"email-pixel">
		<!--email_open_check-->
	</div>

	<!-- default_mail_footer -->
=09
	<div class=3D"default-mail-footer">
	=09
		<div>
	<span>
		Sent via
		<a class=3D"text-muted" href=3D"https://erpnext.com?source=3Dvia_email_fo=
oter" target=3D"_blank" style=3D"color:#687178 !important">
			ERPNext
		</a>
	</span>
</div>
	=09
	</div>
=09

</div>
									</td>
								</tr>
								<tr>
									<td valign=3D"top" style=3D"font-family:-apple-system, BlinkMacSys=
temFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans=
, Helvetica Neue, sans-serif">
										<div class=3D"print-html"></div>
									</td>
								</tr>
							</table>
						</td>
					</tr>
				</table>
			</td>
		</tr>
	</table>
</body>

</html>

--===============7815968053194883997==--

--===============2207987403685055338==--

Execute this from worker-d

For extra_hosts

Hi @revant_one,

Could you please clarify where I should add the extra_hosts directive? All containers can access external sites and return the expected HTML when I run:

curl https://test.menteware.com

But I’m unsure why I might need extra_hosts in this scenario. Any guidance would be greatly appreciated!

Then image issue need more info. You can exec into the container and do bench --site site.name.com console then try to execute the python functions that are causing problems. Tweak code in console to understand why it triggers error.