Troubleshooting SMTP connectivity between Azure Container Apps and Cloudflare Tunnel
On this page4 sections ▾
I was exposing smtp4dev from Azure Container Apps through a Cloudflare Tunnel so a distributed team could use the mail catcher. Its HTTP web UI worked, but SMTP connections timed out. The useful checks were the SMTP listener, Azure's additional TCP port, and the service address used by cloudflared.
Switching to the short app name was part of the working configuration, but the saved tests don't isolate the hostname as the cause.
#The setup
There were two apps in the same Container Apps environment:
mail-catcher-app, runningsmtp4devwith a web UI and an SMTP listener on port2525.tunnel-agent-app, runningcloudflared, with separate published hostnames for the web UI and SMTP service.
HTTP ingress served the web UI, with an additional TCP port for SMTP. A successful request to the web UI only checked the HTTP route; it didn't verify the SMTP port.
#Check the listener and exposed port
The app log said SMTP Server is listening on port 2525 (::). That established that the application had opened a listener, but not that the tunnel agent could reach it.
Check the additional TCP port's target and exposed port separately. The target must match the application's listener; clients connect to the exposed port. Azure defaults the exposed port to the target port when it isn't specified. The examples here use 2525 for both. Microsoft's ingress documentation describes these mappings and the visibility restrictions.
#Test from the tunnel agent
Run the connectivity test inside tunnel-agent-app, where cloudflared makes its connection. My image didn't have telnet or nc, so I used Bash's /dev/tcp support:
# Replace HOST and PORT with the origin hostname and exposed port.
(echo > /dev/tcp/HOST/PORT) &> /dev/null && echo "Success" || echo "Failure"For this setup, the short-name check is:
(echo > /dev/tcp/mail-catcher-app/2525) &> /dev/null && echo "Success" || echo "Failure"Success means a TCP connection opened. It doesn't verify SMTP authentication, message delivery or the client side of the Cloudflare Tunnel. A timeout can also take a while to return; interrupt the check if needed.
If comparing the short name with an FQDN, repeat the test using the exact FQDN from the app's ingress settings and keep the command beside its output. Use the full value from Azure, including the environment identifier and region, rather than an abbreviated example address.
Azure supports app-name addressing within an environment. Its documentation also describes FQDN-based communication within the environment. An .internal. FQDN isn't evidence that traffic went to a public endpoint, and the short name doesn't establish a direct connection to a container's private IP that bypasses ingress. Microsoft's app communication documentation explains the service-discovery and proxy routing involved.
#Configure a TCP origin for SMTP
For the SMTP published route, the origin service should use TCP:
tcp://mail-catcher-app:2525An https:// origin expects HTTP over TLS; it won't route an ordinary SMTP connection. Cloudflare's published application protocols use tcp:// for arbitrary TCP services. Keep the web UI on its own HTTP or HTTPS route.
The published TCP hostname also requires a compatible client connection. For this routing mode, clients use cloudflared access tcp to establish a local connection, then point the SMTP client at that local listener. Publishing the hostname doesn't make it an ordinary public SMTP endpoint.
Once the tunnel agent can reach the TCP origin, test an SMTP submission from the intended client and check that the message appears in smtp4dev. That checks the full path that an HTTP request to the web UI leaves untested.