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.
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.
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
@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?
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?
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.
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.
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?
Make sure images remain backward compatible for different setups. OR else people will upgrade and need some additional tweaks to get new image working.
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
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.
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.
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:
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
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.