The official Docker WordPress image is minimal. It does not have any MTA (Mail Transfer Agent) like Exim4 or Postfix installed by default. So you’ll be unable to send e-mails from your wordpress Docker container without using some external SMTP relay service.
This article describes several solutions for adding Postfix to your system. So you’ll be able to send e-mails from WordPress using SMTP.
Of course, this is a barebone article. It is very minimal. E.g. it does not consider security problems. You’ll need to check the Postfix documentation and security-related articles to secure the system. Here we consider only some minimal functionality to make your SMTP e-mail sending functionality work at all.
1. How to Set up an SMTP Email Server in Docker
1.1 How to Install Postfix in a Separate Container (without DKIM)
In this section, we’ll create a Postfix docker container based on the image tozd/postfix. This solution does not support DKIM verification (digital signature for your e-mails). If you need DKIM, please skip to the next section.
Let’s create the directory
mkdir /opt/projects/mail cd /opt/projects/mail
We’ll need one more directory for our container:
mkdir volumes mkdir volumes/spool mkdir volumes/log
Now let’s add the file
version: '3.8' services: mail: container_name: mail-postfix image: tozd/postfix:alpine-38 restart: always expose: - "25/tcp" - "465/tcp" - "587/tcp" environment: MAILNAME: example.com # replace with your own server name instead of example.com MY_NETWORKS: 172.17.0.0/16 127.0.0.0/8 192.168.0.0/16 # my mail server happened to be in the 192.168.0.0/16 so I had to add this network MY_DESTINATION: localhost.localdomain, localhost ROOT_ALIAS: email@example.com # replace with your e-mail address volumes: - /opt/projects/mail/volumes/spool:/var/spool/postfix - /opt/projects/mail/volumes/log:/var/log/postfix networks: mail: networks: mail: name: mail # or it will be created as mail_mail
Also it is a good idea to check the IP range (subnet) for the network
docker inspect mail
In my case the subnet was
192.168.128.0/20. So I have added
192.168.0.0/16 to the
MY_NETWORKS variable to avoid problems with such networks. Without it I was getting the error “Relay access denied” when trying to send an e-mail from my WordPress container.
Another way is to explicitly set the subnet for the network
ipam section in the docker-compose documentation (ipam because IP Address Management driver is used). The file
/opt/projects/mail/docker-compose.yml for this case could look like this:
version: '3.8' services: mail: container_name: mail-postfix image: tozd/postfix:alpine-38 restart: always expose: - "25/tcp" - "465/tcp" - "587/tcp" environment: MAILNAME: example.com # replace with your own server name instead of example.com MY_NETWORKS: 172.16.0.0/12 127.0.0.0/8 MY_DESTINATION: localhost.localdomain, localhost ROOT_ALIAS: firstname.lastname@example.org # replace with your e-mail address volumes: - /opt/projects/mail/volumes/spool:/var/spool/postfix - /opt/projects/mail/volumes/log:/var/log/postfix networks: mail: # ipv4_address: 172.30.0.2 # there is no need to set the container IP in the network `mail` explicitly, # but you can do it if you wish networks: mail: name: mail # or it will be created as mail_mail ipam: driver: default config: - subnet: "172.30.0.0/24"
I did not assign a static IP to the
mail-postfix container in this example. You can do this if you wish. But please notice that at the time of writing you can not assign an IP to the subnet gateway in
docker-compose version 3. It is unavailable in version 3 of
docker-compose yet (available in version 2 though).
And one more way to deal with the network IP range assignment problem is to set explicitly the IP address pool in which Docker is allowed to create networks. This approach is described in this article. And it consists in setting the default address pool in the file
docker-compose.yml above creates the network
docker-compose up -d
To stop/remove the container we could use:
1.2 How to Install Postfix in a Separate Container (with DKIM)
In this section, we’ll create a Postfix docker container based on the image catatnight/postfix. This image supports DKIM verification.
Let’s add the file
version: '3.8' services: mail: container_name: mail-postfix image: catatnight/postfix restart: always expose: - "25" environment: maildomain: wpdiaries.com # replace with your own server name instead of example.com smtp_user: admin:pwd # username and password for SMTP volumes: - /opt/projects/mail/volumes/domainkeys:/etc/opendkim/domainkeys # - /opt/projects/mail/volumes/log/supervisor:/var/log/supervisor networks: mail: # ipv4_address: 172.30.0.2 networks: mail: name: mail # or it will be created as mail_mail ipam: driver: default config: - subnet: "172.30.0.0/24" # 172.16.0.0 – 172.31.255.255
Run the container:
docker-compose up -d
Now we need to generate public and private keys for DKIM. The procedure of generating files (though not for a docker container) is described in detail in this article.
Let’s enter the running container terminal:
docker exec -it mail-postfix bash
Generate the keys in the directory
/etc/opendkim/domainkeys inside the container (replace
example.com with your mail domain name):
cd /etc/opendkim/domainkeys sudo opendkim-genkey -s mail -d example.com sudo chown opendkim:opendkim mail.private
-s defines a selector here. It could be any string. Often the current year is used as a selector. We used the word
The command above will generate 2 files :
mail.txt – our public key
mail.private – our private key
and will change the owner for the private key file.
Exit the container
Now the file on our mounted (in
/opt/projects/mail/volumes/domainkeys/mail.txt will contain something similar to this:
mail._domainkey IN TXT ( "v=DKIM1; k=rsa; " "p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCvhz766AiMeVPzidwF+wXzDM6z+wt4g8pOR2VoxPwa4wfBL3A/lqNopbCBMBpDB6mEDzDUmWMw8r6NjKRulBMqE5tq470nvaNR05w7MiM3RjrhSwVjPDMhA46HNOSSf26hTrtnMqpuNCIidGpH3W132h6sGtc6YuMTByKDZe77+QIDAQAB" ) ; ----- DKIM key mail for example.com
So we will have to add this TXT record with the following parameters on the DNS server for our mail domain:
Record type: TXT Host: mail._domainkey.example.com Value: v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCvhz766AiMeVPzidwF+wXzDM6z+wt4g8pOR2VoxPwa4wfBL3A/lqNopbCBMBpDB6mEDzDUmWMw8r6NjKRulBMqE5tq470nvaNR05w7MiM3RjrhSwVjPDMhA46HNOSSf26hTrtnMqpuNCIidGpH3W132h6sGtc6YuMTByKDZe77+QIDAQAB
E.g. the DKIM TXT record for wpdiaries.com on the Namecheap DNS server looks like this:
Then we’ll need to restart our docker container
cd /opt/projects/mail/ docker-compose restart
For more detailed information about adding a DKIM key (but without Docker) please see this article.
1.3 How to Add Postfix Directly to the WordPress Image
This section has been added for educational purposes only. So you can safely skip it if you wish. You shouldn’t add Postfix directly to the official wordpress image. Why? Because it is not recommended to have more than 1 responsibility for 1 docker container. Please see the section Decouple applications in the official documentation on Docker best practices.
So please do not use the approach described in this section. It is given here for educational purposes only.
Still, if you are interested in this subject, you could use a
Dockerfile like this:
FROM wordpress:5.7-php8.0-apache # Install packages under Debian RUN apt-get update # Install Postfix (replace wpdiaries.com with your own domain name) RUN ["/bin/bash", "-c", "debconf-set-selections <<< \"postfix postfix/mailname string wpdiaries.com\""] RUN ["/bin/bash", "-c", "debconf-set-selections <<< \"postfix postfix/main_mailer_type string 'Internet Site'\""] RUN apt-get install --assume-yes postfix # Uncomment the following line if you need inter-container communication on port 25 #EXPOSE 25 COPY entry.sh / RUN chmod +x /entry.sh ENTRYPOINT ["/entry.sh"]
entry.sh is a script like this:
#!/bin/bash service postfix start # Similar to how Apache2 is run in the official wordpress image Dockerfile: /usr/local/bin/docker-entrypoint.sh apache2-foreground
docker-compose.yml instead of the
image directive you will need to use the
build directive like this:
build: context: .
Please notice that in our entry script
entry.sh first we run Postfix and then we execute a command similar to the ENTRYPOINT directive of the official wordpress image Dockerfile. We do it like this because according to the official documentation on ENTRYPOINT and on CMD, if multiple such directives exist, only the last one is executed. And so, despite the wordpress image (on which this image has been based) already has an ENTRYPOINT directive, this first directive will be ignored. So we need the ENTRYPOINT command in our Dockerfile to execute all we need (in our case, to run Postfix) plus run a directive similar to the one from the official wordpress image Dockerfile.
For more information on Dockerfiles please see the Dockerfile reference.
Again, having Postfix right inside the wordpress container is not recommended. This section of the article is given for educational purposes only. Please see the sections above instead.
1.4 How to Send Emails Using Host Machine SMTP
Some people suggest configuring Postfix on the host machine. Then you could send e-mails from Docker using your host machine MTA (Mail Transfer Agent). This solution is discussed here.
The reason behind such a solution could be that you could possibly need some sending functionality on your host machine anyway (maybe some service or automatic system notification e-mails). In this case why set one more SMTP server in Docker.
But if you, like me, do not need to send e-mails from your host machine, then I would keep the SMTP functionality inside a Docker container.
Personally I prefer to install as much functionality as possible in Docker containers. It simplifies deployment really much. And looks a little bit cleaner to me. But the final decision is up to you of course.
2. How to Configure Docker WordPress Container to Use Postfix
In the previous sections, we’ve considered how to install Postfix in a separate docker container. In both scenarios, the container with Postfix creates the network
Now we need to add the network
docker-compose.yml files of our WordPress projects where we need the mail sending functionality :
version: '3.8' services: wordpress: image: wordpress # More configuration options here # ... networks: mynetwork1: mynetwork2: # We are adding the network `mail` here: mail: networks: mynetwork1: mynetwork2: # We are adding the network `mail` here: mail: external: true # which also means this network must exist already before this container is run name: mail # the name is nnecessary or the container will look for currentdirectoryname_mail
These WordPress containers will join the network
3. How to Configure Mail in WordPress Admin Panel
In user-defined networks (we have the network
my-wordpress-container, we can log into bash of this
my-wordpress-container like this:
docker exec -it my-wordpress-container bash
Then in the container bash we can install the necessary packages and run the ping command (the example is given for a container built on the official
wordpress image which runs Debian):
apt-get update apt-get install iputils-ping ping mail-postfix
You’ll see that you can ping the container
mail-postfix by its container name from another container running in the same network
So, in our case, since the Postfix container name is
mail-postfix, we’ll use this name instead of
localhost in the mail configuration.
If you use e.g. Post SMTP plugin for mail configuration, the outgoing mail server configuration screen in the plugin settings could look like this:
Of course, we could set a static IP address for the container
mail-postfix (see the commented line for
ipv4_address in the example above) and use this IP address instead. But using the container name is probably more convenient.
Also in the wizard, if we have chosen to build on the image catatnight/postfix, we will need to enter the user name and password for the SMTP server (which we have set previously in the
Ii we have built on the image tozd/postfix, we do not enter the password (authentication without a password is used there).
The plugin Post SMTP allows us to send a test e-mail. So, send an e-mail to the e-mail address
email@example.com. You’ll get a response with the information if your e-mail has been set correctly and what could be improved to increase your mail deliverability. If everything is OK, the reply e-mail will contain the following lines:
4. E-mail Related DNS/rDNS Records
There are several mail-related DNS records. Some of them increase mail deliverability and some don’t.
E.g. if you plan to work with your mail on-line, you would need a web mail client for it. You would need a subdomain where this mail client will be available. E.g. mail.example.com. And you will need an A record added for such a subdomain at your DNS server. This record will be mail-related. But it does not influence your mail deliverability in any way.
An MX record is also not necessary for sending mail. If you plan to accept e-mails (e.g. by having a POP3 or IMAP email server), you will need an MX record. But if you are only sending e-mails via SMTP (you do not accept e-mails at the server, just sending) you don’t need an MX record. An MX record does not influence the outgoing mail delivery.
But you certainly need a PTR record. Please see this post for details.
Please keep reading for more details on mail-related DNS/rDNS records.
We’ll consider an MX record here only because it is mail-related. You do not need an MX record for sending mail. You need it for accepting mail only. So if you need only e-mail sending functionality, you don’t need to set this record. And can safely skip to the next section.
To check if your mail domain (or main domain if you do not have a special mail subdomain set) already has an MX record set for it run (on Linux or under Mac):
dig MX example.com +short
There are some special cases when you could have more than 1 MX record for 1 domain. But usually, you do not need it. So if you already have an MX record, normally there is no need to add another one. 2 mail providers will be unable to handle mail for 1 domain at the same time anyway (only 1 of them will do it at a time).
Adding an MX record is very straightforward. Normally you could find a help page for your DNS server describing how to do that. E.g. adding an MX repost at Namecheap DNS server is described here.
A PTR record (other names: a pointer record, a Reverse DNS record, an rDNS record) is used for reverse DNS lookup. In mail functionality, this is the record by which a mail server (at the mail receiving side) finds your sender mail server domain name by its IP address. You need to add this record if you are sending e-mails and you want to increase mail deliverability.
Normally you don’t set a PTR on your DNS server. You set it at your IP address provider (usually your hosting company). Probably you’ll need to google on how to set the PTR record for your servers at your hosting company. E.g. how to set a PTR record at Hetzner is described here and here, for DigitalOcean it is described here, etc. Normally you can easily find how to set the PTR record at your hosting company servers by running a Google query like “<my hosting company name> set ptr record”.
You can check the PTR record you have set for your server currently (please substitute your server IP address in the commands below):
under Linux and under Mac:
dig -x 18.104.22.168 +short
To increase your mail deliverability set the PTR record for your sending mail server correctly (e.g. for this site it is set for
wpdiaries.com since I did not need a separate mail-related subdomain here).
For more information on PTR records please see this article on namecheap.com.
An SPF (Sender Policy Framework) record is necessary to decrease e-mail spoofing (a technique when spammers send SPAM e-mails using your domain e-mail in the FROM: field).
You can use a wizard like spfwizard.com to help you to form an SPF record.
E.g. for wpdiaries.com I checked if I had any current SPF record already:
dig TXT wpdiaries.com +short
And found out NameCheap (which provides registrar, DNS and e-mail forwarding services for my domains) had one generated for me already:
v=spf1 include:spf.efwd.registrar-servers.com ~all
So using spfwizard.com and taking into account that sometimes I reply via the Google SMTP server (and I did not want my e-mails to be rejected), for wpdiaries.com I have generated the following SPF record:
wpdiaries.com. IN TXT "v=spf1 a ptr include:spf.efwd.registrar-servers.com include:_spf.google.com ~all"
After forming an SPF record for your domain, you’ll need to add the record as a usual TXT record at your DNS server.
E.g. the SPF TXT record for wpdiaries.com on the Namecheap DNS server looks like this:
Then you can use one of SPF record validators (e.g. this one) to check if your SPF record exists and has correct format.
You could also check this article for information about SPF records. I think it is pretty useful.
The DKIM (DomainKeys Identified Mail) is a digital signature added to your e-mails. You need this record to increase mail deliverability.
We have considered adding a DKIM record for Postfix when discussed using the image catatnight/postfix right above.
I hope you’ve enjoyed the article.
Do you have any questions?
Or maybe you have any ideas on what I could add to the article?
Or maybe you could tell me which subjects you’d like to read about in future articles?
In this case, please do not hesitate to post a comment below.
I would be very glad to hear from you.