Introduction
I've been working on a new version of the plugin that allows people to subscribe to UConn Today. One of the goals was to see how the plugin handles sending emails to a listserv which has the subscriber list. I wanted something that was going to be easy to incorporate into the project which uses Docker.
The goal is to be able to send an email from the web server and then see it arrive at the mailhog SMTP server. After a lot of research (and not having any idea what I was doing), I found these two articles:
The architecture for testing the emails is to use two containers
- A web server with PHP that is configured to send emails using mhsendmail ("A sendmail replacement which forwards mail to an SMTP server."). This will be an extension of the php 7.2 docker image we use in all of our projects.
- An SMTP server image that uses mailhog which is "is an email testing tool for developers".
Updating the web server
Because we don't necessarily need email in every project, I decided create a new image based on our uconn/php72-official image. Based on the articles above, the other steps were pretty straightforward. But I got a little hung up on how Docker networking works so I'll describe the kinds of errors I got and how I fixed them. But first, here's the Dockerfile I used.
FROM uconn/php72-official
COPY mhsendmail/php.ini /usr/local/etc/php/php.ini
RUN apt-get update
&& apt-get install --no-install-recommends -y ca-certificates curl git nano
&& rm -rf /var/lib/apt/lists/*
&& curl -Lsf 'https://storage.googleapis.com/golang/go1.8.3.linux-amd64.tar.gz' | tar -C '/usr/local' -xvzf -
ENV PATH /usr/local/go/bin:$PATH
RUN go get github.com/mailhog/mhsendmail
&& cp /root/go/bin/mhsendmail /usr/local/bin/mhsendmail
Unlike in the articles I decided to keep the php.ini file in the repo for reference.
Docker Networking
Once I'd made a new image called mailhog-test
, it was time to update my docker-compose file. So again, following the tutorials, I updated it like this.
version: "3"
services:
web:
image: mailhog-test:latest
environment:
# wordpress env variables
ports:
- "80:80"
volumes:
# a bunch of volumes for wordpress
links:
- db:mysql
privileged: true
# the mailhog server
mail:
image: mailhog/mailhog:latest
ports:
- "1025:1025"
- "8025:8025"
# other services below...
Next, I went into the web server and tried to send an email with php.
$ php -a
> mail("to.receiver@test.com", "Subject line", "message");
But then I got....
Errors
The two most common errors were dial tcp 127.0.0.1:1025: getsockopt: connection refused
and dial tcp: lookup mailhog on 127.0.0.11:53: no such host
depending on how I tried to configure my hosts file. I could see that php was trying to send the email because of the port 1025 on localhost. But it wasn't getting there. But then it hit me!
Solution
When containers need to communicate with each other inside their network, they need to be referenced by the names of their services. In this case web
and mail
respectively. I was faithfully following the examples and had included this line in my php.ini file - sendmail_path = /usr/bin/mhsendmail --smtp-addr mailhog:1025
. Note that it says mailhog:1025
as the address to go to. When I changed that to mail:1025
(the name of the service) it worked perfectly.
Conclusion
When working with Docker it always helps to try and think about what's going on with the application from Docker's perspective.
- What's going on inside those containers?
- What information is needed by the network?
- Where are files and data trying to go?
Even though I'd networked other containers to each other, because I had no prior experience with email, I forgot to think about how the network worked. Fortunately, we now have an easy way to test email and see them come up in mailhog.