I packaged a standard application (think of it as a standard PHP or <insert your preferred framework here>) into a Docker container. So far, it was working flawlessly, but then a problem arose: send an email from the Docker container (the event is triggered within the container).
As you may know, a good Docker container is a container with only one process running: the naive solution for our case would be to have, in addition to having our PHP process running, another process to manage the email interexchange (an MTA, i.e. Postfix). As we are following the best practices for Docker containers, this path is discouraged.
There are many solutions to this problem.
The common ground for all of the solutions is to rely
ssmtp is a simple
Provided that the container distribution ships
ssmtp, the installation is straightforward: just add the package during the install phase of the Dockerfile.
ssmtp must be configured to relay every email an SMTP host, e.g.:
# cat /etc/ssmtp/ssmtp.conf # The user that gets all the mails (UID < 1000, usually the admin) root=postmaster # The place where the mail goes. The actual machine name is required # no MX records are consulted. Commonly mailhosts are named mail.domain.com # The example will fit if you are in domain.com and you mailhub is so named. # Use SSL/TLS before starting negotiation UseTLS=Yes UseSTARTTLS=Yes # Fill the following with your credentials (if requested) AuthUserfirstname.lastname@example.org AuthPass=supersecretpassword # Change or uncomment the following only if you know what you are doing # Where will the mail seem to come from? # rewriteDomain=localhost # The full hostname # hostname="localhost" # The address where the mail appears to come from for user authentication. # rewriteDomain=localhost # Email 'From header's can override the default domain? # FromLineOverride=yes
All the three solutions that I am going to illustrate rely on having a
Let’s review each solution.
An external SMTP relay host
If an external SMTP relay host is available, the solution is to point
mailhub option of
ssmtp to the external SMTP host.
Another container running the MTA
The proper way to solving this problem would be to run a Docker container just for the MTA itself (personal preference: Postfix). One caveat of this solution: some Linux distributions come with an MTA running out of the box. If the container host is already running an MTA, the container cannot
By searching on GitHub, a promising and an up-to-date container is
# docker run --link=postfix-container my-awesome-app-that-needs-an-mta
The container must
postfix-container(or the name defined as the
Relying on the host MTA
Premise: the Docker daemon exposes an adapter to all the containers running on the same host. This adapter is usually named as the
# ip a show docker0 5: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether 11:22:33:44:55:66 brd ff:ff:ff:ff:ff:ff inet 172.17.42.1/16 brd 172.17.255.255 scope global docker0 valid_lft forever preferred_lft forever inet6 fe80::11:22ff:fff0:3344/64 scope link valid_lft forever preferred_lft forever
If the host MTA is listening on
docker0 interface, then the containers can relay email to the host MTA. There is not an extra configuration on the container itself, just
docker0 IP as
EXTRA: HOW TO CONFIGURE POSTFIX TO LISTEN ON DOCKER INTERFACE (LIKE DOCKER0) AS WELL
To use the solution
On the host, open
/etc/postfix/main.cf and add the
docker0 IP to the
inet_interfaces option and add the subnetwork block range of the containers that need to use the host MTA to the
# cat /etc/postfix/main.cf [...] inet_interfaces = 127.0.0.1, 172.17.42.1 mynetworks = 127.0.0.0/8 172.17.42.0/24 [...]
If Postfix is set to be started at boot by systemd, we need to take care of the dependency: Docker daemon must be started before the Postfix daemon, as Postfix needs to bind on the
docker0 IP address.
In order to express this dependency, and luckily for us, systemd already ships with a service that detects when an interface is up:
# systemctl | grep docker0 sys-devices-virtual-net-docker0.device loaded active plugged /sys/devices/virtual/net/docker0
Postfix must be started after the
# systemctl | grep postfix postfix.service loaded active exited Postfix Mail Transport Agent postfix@-.service loaded active running Postfix Mail Transport Agent (instance -)
in this case of both of the Postfix services with:
# systemctl edit postfix.service postfix@-.service
Override the unit service file by declaring the dependency explicitely:
[Unit] Requires=sys-devices-virtual-net-docker0.device After=sys-devices-virtual-net-docker0.device
Reload systemd with
systemctl daemon-reload and restart Postfix with
systemctl restart postfix.
Relying on the host MTA by using
host network driver on Docker
When a container is set to use host networking interface, the container can access the host networking and thus its services. If the container host already has an MTA configured, then the containers can use it by just pointing to
localhost.The syntax to use host networking interface for the application that needs to use the host MTA is:
# docker run --net=host my-awesome-app-that-needs-an-mta
ssmtp, just point the
NOTE: Using the host networking interface has obviously security