docker-library / wordpress

Docker Official Image packaging for WordPress
https://wordpress.org/
GNU General Public License v2.0
1.77k stars 1.06k forks source link

Email does not work #30

Open md5 opened 9 years ago

md5 commented 9 years ago

As it currently stands, anything in Wordpress that needs to send email is broken. The wp_mail function that is used for sending out email by default is a thin wrapper over PHP's mail function, which defaults to calling /usr/sbin/sendmail -t -i (cf. http://php.net/manual/en/mail.configuration.php). There are php.ini settings for SMTP, but they are only used on Windows.

For my own work based on this image, I've been installing ssmtp and configuring it to use an external relay host for SMTP, but that's clearly not a turnkey solution for everyone. It also looks like there are many Wordpress plugins that provide SMTP support, but I don't think that installing any of them by default in this image seems reasonable.

Perhaps all of this should just be documented, but I wanted to get it on your radar.

drzraf commented 6 years ago

Any chance to see @pierreozoux plugin been bundled and configurable via environment? Those who want to do it the docker way can still use an external docker image for the MTA, but the WP plugin/hooks would be needed anyway.

eduardo-marcolino commented 6 years ago

@lazyfrosch thank you !

orlando commented 6 years ago

This is what I ended up doing to get working SMTP with environment variables, I'm appending this code to wp-config.php

/* SMTP Settings */
add_action( 'phpmailer_init', 'mail_smtp' );
function mail_smtp( $phpmailer ) {
  $phpmailer->isSMTP();
  $phpmailer->Host = getenv('WORDPRESS_SMTP_HOST');
  $phpmailer->SMTPAutoTLS = true;
  $phpmailer->SMTPAuth = true;
  $phpmailer->Port = getenv('WORDPRESS_SMTP_PORT');
  $phpmailer->Username = getenv('WORDPRESS_SMTP_USERNAME');
  $phpmailer->Password = getenv('WORDPRESS_SMTP_PASSWORD');

  // Additional settings
  $phpmailer->SMTPSecure = "tls";
  $phpmailer->From = getenv('WORDPRESS_SMTP_FROM');
  $phpmailer->FromName = getenv('WORDPRESS_SMTP_FROM_NAME');
}

You can check how it's integrated in the Dockerfile in this repo (https://github.com/debtcollective/docker-wordpress/)

controlcde commented 6 years ago

@orlando: Could you please send a PR to get your code into the project? Sounds like the integration of @lazyfrosch code base.

orlando commented 6 years ago

@controlcde not sure if they want to merge something like this. but if they want to, it's easier to modify the Dockerfile and the main script instead

naum commented 6 years ago

Attempted the solution that @janpapenbrock graciously provided and sendmail is setup and appears to work from PHP WP functions but mail internally fails with a "SMTP; 550 Host unknown" diagnostic code.

What am I missing?

benyanke commented 6 years ago

Can confirm @orlando 's solution works. Any chance this might be committed to the repo? Would the admins be open to a PR?

pcmanprogrammeur commented 6 years ago

Here is my solution in the Dockerfile:

RUN apt-get install -y ssmtp RUN echo "sendmail_path = /usr/sbin/ssmtp -t" >> /usr/local/etc/php/conf.d/sendmail.ini RUN sed -i -e 's/mailhub=mail/mailhub=[MY-RELAY-IP]/' /etc/ssmtp/ssmtp.conf RUN sed -i -e 's/#rewriteDomain=/rewriteDomain=[MY-RELAY-IP]/' /etc/ssmtp/ssmtp.conf RUN sed -i -e 's/#FromLineOverride=/FromLineOverride=/' /etc/ssmtp/ssmtp.conf RUN sed -i -e '/hostname=/d' /etc/ssmtp/ssmtp.conf

sgohl commented 6 years ago

ssmtp is propably the best solution as it is very small, does not require a service to be running and installs as the sendmail binary. If the official Wordpress image would include this package, then the entrypoint script could populate the ssmtp.conf via ENV variables at container start.

Can't you guys don't just install ssmtp with the official Image? Then we all would be satisfied and wouldn't have to maintain child images. We'd mount our ssmtp.conf in the container and voila.

controlcde commented 5 years ago

I try to realize the configuration with WORDPRESS_CONFIG_EXTRA but the $phpmailer variable will be resolved (or not in this case)

WARNING: The phpmailer variable is not set. Defaulting to a blank string.

and the config looks like

// WORDPRESS_CONFIG_EXTRA
/* SMTP Settings */
add_action( 'phpmailer_init', 'mail_smtp' );
function mail_smtp(  ) {
->isSMTP();
->Host = 'mailServer';
->SMTPAutoTLS = true;
->SMTPAuth = true;
->Port = 587;
...

How could I escape the variable $phpmailer?

yosifkit commented 5 years ago

@controlcde, Are you setting WORDPRESS_CONFIG_EXTRA via a compose or stack file? If so you would need to put $$phpmailer so that docker-compose / docker stack will put a literal $ rather than trying to replace the variable in the yaml. (https://docs.docker.com/v17.09/compose/compose-file/#variable-substitution)

controlcde commented 5 years ago

@yosifkit You're right. With the Double-$ the correct entries will be made in the config file. But it doesn't work - I think the WORDPRESS_CONFIG_EXTRA should be add at the end of wp-config.php to get the email settings working.

packoman commented 5 years ago

Hi @pcmanprogrammeur, @timwsuqld, @md5, (sorry for spamming you guys, but I am bit desperate) I am trying to follow your suggestions here to setup E-Mail in a Wordpress container using ssmtp. Unfortunately without success thusfar. Here is what I have:

wordpress docker file content:

FROM wordpress:4.9

RUN apt-get update
RUN apt-get install -y ssmtp

COPY ssmtp.conf /etc/ssmtp/ssmtp.conf

RUN echo "sendmail_path = /usr/sbin/ssmtp -t" >> /usr/local/etc/php/conf.d/sendmail.ini

ssmtp.conf:

root=myuser@gmail.com
mailhub=smtp.gmail.com:587
AuthUser=myuser
AuthPass=mypassword
UseSTARTTLS=Yes
UseTLS=Yes
hostname=localhost

When I test things out by requesting a password recovery E-mail I get the following error message in the browser:

The email could not be sent. Possible reason: your host may have disabled the mail() function.

with this URL: https://mydomain.com/wp-login.php?action=lostpassword

However I can send mails using ssmtp by logging into the container using

docker exec -it my_wordpress_docker_container /bin/bash

and doing

echo "This message is sent correctly!" | ssmtp -vvv myuser@gmail.com

Can anyone tell me what I am missing? Any help is much appreciated, thanks!

ClashTheBunny commented 5 years ago

@packoman I had the same problem when I didn't have the sendmail.ini in place before php start. Also, I've seen many places that they put quotes around the command: RUN echo "sendmail_path = \"/usr/sbin/ssmtp -t\"" >> /usr/local/etc/php/conf.d/sendmail.ini

igorivaniuk commented 5 years ago

@packoman need allow www-data to send mail

FROM wordpress:4.9

RUN apt-get update
RUN apt-get install -y ssmtp

COPY ssmtp.conf /etc/ssmtp/ssmtp.conf
COPY revaliases /etc/ssmtp/revaliases

RUN echo "sendmail_path = /usr/sbin/ssmtp -t" >> /usr/local/etc/php/conf.d/sendmail.ini

revaliases

root:myuser@gmail.com:smtp.gmail.com:465
www-data:myuser@gmail.com:smtp.gmail.com:465

Also for gmail read this:

https://support.google.com/mail/answer/7126229

https://www.google.com/accounts/DisplayUnlockCaptcha

falconmick commented 5 years ago

I found the following postfix relay: https://github.com/wader/postfix-relay

I found the maintainer still is working on it and it has support for DKIM which is all i needed. I just added to the functions.php file the following:

add_action( 'phpmailer_init', 'wpse8170_phpmailer_init' );
function wpse8170_phpmailer_init( PHPMailer $phpmailer ) {
    // connect to the SMTP server running in our internal network
    $phpmailer->Host = 'smtp';
    $phpmailer->Port = 25;
    $phpmailer->SMTPAuth = false;
    $phpmailer->SMTPSecure = false;
    $phpmailer->IsSMTP();
}

Where smtp is the name of my postfix relay container (which thus resolves to the correct IP without exposing to the internets)

I then used the following config inside of my docker-compose.yml to get the image up n running

  volumes:
    dkim-data:

  wordpress:
    ...
    depends_on:
      ...
      - smtp # added depends_on because wordpress depends on it.. that being said probably not required
    ...

  smtp:
    image: mwader/postfix-relay
    restart: always
    container_name: sova-smtp
    environment:
      - POSTFIX_myhostname=awesomesite.com.au
      - OPENDKIM_DOMAINS=awesomesite.com.au
    volumes:
      - dkim-data:/etc/opendkim/keys
    expose:
      - "25"
joeldeteves commented 5 years ago

I would consider msmtp instead. It is probably a little better than ssmtp, because ssmtp afaik is unmaintained.

https://marlam.de/msmtp/ https://wiki.archlinux.org/index.php/msmtp

Kantankerus commented 5 years ago

I would consider msmtp instead. It is probably a little better than ssmtp, because ssmtp afaik is unmaintained.

https://marlam.de/msmtp/ https://wiki.archlinux.org/index.php/msmtp

@joeldeteves I researched this separately and have come to the same conclusion. msmtp seems to be the way forward and fills largely the same exact role as ssmtp, only better (fewer bugs, improved system compatability) and more recently maintained. It appears Arch has completely dropped ssmtp from its repositories and I see Cygwin apparently supports msmtp now as well, which was apparently one of the platforms keeping ssmtp alive.

Adrien-Luxey commented 4 years ago

(Edited because I was being aggressive.) I'd like to see a mention of the missing emailing in the image's README!

yosifkit commented 4 years ago

@Adrien-Luxey, Not a very inviting tone to take on an old issue; please be respectful.

If you have concrete suggestions for how it can be improved, a PR is welcome (here is the source of the Docker Hub description: https://github.com/docker-library/docs/blob/master/wordpress/content.md)

Adrien-Luxey commented 4 years ago

You are right, please forgive me. I'll propose a (gentle) modification to the README myself. I will edit my previous comment if you don't mind.

JoelLinn commented 4 years ago

Don't use ssmtp on new setups. It isn't serviced and debian packages are orphaned. Use msmtp instead.

/etc/msmtprc

# Set default values for all following accounts.
defaults
auth           on
tls            on
tls_trust_file /etc/ssl/certs/ca-certificates.crt
logfile        /tmp/msmtp.log

# Custom Mailserver
account        mymailserver
host           mail.example.com
port           587
from           user+wordpress@example.com
user           user@example.com
password       12345678

# Set a default account
account default : mymailserver

With recent versions (not yet available in alpine and debian) you can also set set_from_header to overwrite wordpress@localhost from address.

Also set this php configuration value: sendmail_path = "/usr/bin/msmtp -t"

Adrien-Luxey commented 4 years ago

I agree with the above. Let me propose my simple configuration that only forwards mail to my host's postfix (you could also have a dedicated container for sending mail, which would alleviate the need for fixed container network configuration):

/etc/msmtprc (container)

account default
host 172.27.0.1 # IP of my host: the network's gateway from the container's point of view
port 25
from php@yourdomain.tld

You also need to configure your host's postfix to accept requests from docker:

/etc/postfix/main.cf (host)

# [...]
mynetworks = ... 172.27.0.0/24

This is minimal: expect your mails to end in spam folders. But it works! Your comments are welcome.

EDIT: I ran into trouble setting mynetworks = ... 172.0.0.0/8. Be specific with your network addresses!

wpdiaries commented 4 years ago

I had the same problem. So I tried to find a solution. I've found several solutions and tested all of them at my site (to make sure they worked). It would be too long to post them here. So I have created a separate article:

https://www.wpdiaries.com/mail-functionality-for-official-docker-wordpress-image/

The article contains detailed examples of adding Postfix in a separate container. There are 2 such examples based on 2 different images (one has digital signing e-mails with DKIM and the other one works without DKIM). Also, I have added an example of adding Postfix directly to a WordPress container, but this solution is not recommended (it is better to have 1 responsibility for 1 container as it is described in the official Docker best practices).

I've made sure the e-mail sending functionality works in each of the listed examples.

I hope this will be useful for people who got problems with sending e-mail from WordPress in Docker. At least I did my best to provide as detailed and clear examples as I could.

benhadad commented 4 years ago

possibly use Mailhog? Other images have used this dockerfile:

Forward Message to mailhog

RUN curl --location --output /usr/local/bin/mhsendmail https://github.com/mailhog/mhsendmail/releases/download/v0.2.0/mhsendmail_linux_amd64 && \ chmod +x /usr/local/bin/mhsendmail RUN echo 'sendmail_path="/usr/local/bin/mhsendmail --smtp-addr=mailhog:1025 --from=no-reply@gbp.lo"' > /usr/local/etc/php/conf.d/mailhog.ini

docker-compose: mailhog: container_name: ${APP_NAME}-mailhog image: mailhog/mailhog ports:

joeldeteves commented 3 years ago

I would consider msmtp instead. It is probably a little better than ssmtp, because ssmtp afaik is unmaintained.

https://marlam.de/msmtp/ https://wiki.archlinux.org/index.php/msmtp

Hello, I have found a much better solution than using msmtp, ssmtp, mailhog or any other SMTP client for that matter.

Wordpress is apparently bundled with PHPMailer - I did not realize that it came included with Wordpress by default.

This means with a little creativity we are able to configure it using the WORDPRESS_CONFIG_EXTRA environment variable that is baked into the docker image.

Here is an example written in YAML for our Kubernetes deployment:

          env:
            - name: SMTP_HOSTNAME
              value: "smtp.office365.com"
            - name: SMTP_PORT
              value: "587"
            - name: SMTP_USER
              value: "wordpress@example.com"
            - name: SMTP_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: "wordpress-smtp-password"
                  key: smtp-password
                  optional: true
            - name: SMTP_FROM
              value: "wordpress@example.com"
            - name: WORDPRESS_CONFIG_EXTRA
              value: |
                // SMTP Settings
                require_once( ABSPATH .'wp-includes/plugin.php' );
                add_action( 'phpmailer_init', 'mail_smtp' );
                function mail_smtp( $phpmailer ) {
                  $phpmailer->isSMTP();
                  $phpmailer->Host = getenv('SMTP_HOSTNAME');
                  $phpmailer->Port = getenv('SMTP_PORT');
                  $phpmailer->Username = getenv('SMTP_USER');
                  $phpmailer->Password = getenv('SMTP_PASSWORD');
                  $phpmailer->From = getenv('SMTP_FROM');
                  $phpmailer->FromName = getenv('SMTP_FROM_NAME');

                  // Additional settings
                  $phpmailer->SMTPAuth = true;
                  $phpmailer->SMTPSecure = "tls";
                  $phpmailer->SMTPAutoTLS = true;
                }
                // Prevent Wordpress from overriding the SMTP FROM address (Office 365 compatibility)
                add_filter( 'wp_mail_from', function( $email ) {
                  return $_ENV["SMTP_FROM"];
                });

Of course, with a little tweaking you can apply the same setup in Docker, docker-compose and more, and at least in our case this eliminates the need for building a custom Docker image or running an MTA in a separate container.

Hope this helps,

derspotter commented 3 years ago

I would consider msmtp instead. It is probably a little better than ssmtp, because ssmtp afaik is unmaintained. https://marlam.de/msmtp/ https://wiki.archlinux.org/index.php/msmtp

Hello, I have found a much better solution than using msmtp, ssmtp, mailhog or any other SMTP client for that matter.

Wordpress is apparently bundled with PHPMailer - I did not realize that it came included with Wordpress by default.

This means with a little creativity we are able to configure it using the WORDPRESS_CONFIG_EXTRA environment variable that is baked into the docker image.

Here is an example written in YAML for our Kubernetes deployment:

          env:
            - name: SMTP_HOSTNAME
              value: "smtp.office365.com"
            - name: SMTP_PORT
              value: "587"
            - name: SMTP_USER
              value: "wordpress@example.com"
            - name: SMTP_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: "wordpress-smtp-password"
                  key: smtp-password
                  optional: true
            - name: SMTP_FROM
              value: "wordpress@example.com"
            - name: WORDPRESS_CONFIG_EXTRA
              value: |
                // SMTP Settings
                require_once( ABSPATH .'wp-includes/plugin.php' );
                add_action( 'phpmailer_init', 'mail_smtp' );
                function mail_smtp( $phpmailer ) {
                  $phpmailer->isSMTP();
                  $phpmailer->Host = getenv('SMTP_HOSTNAME');
                  $phpmailer->Port = getenv('SMTP_PORT');
                  $phpmailer->Username = getenv('SMTP_USER');
                  $phpmailer->Password = getenv('SMTP_PASSWORD');
                  $phpmailer->From = getenv('SMTP_FROM');
                  $phpmailer->FromName = getenv('SMTP_FROM_NAME');

                  // Additional settings
                  $phpmailer->SMTPAuth = true;
                  $phpmailer->SMTPSecure = "tls";
                  $phpmailer->SMTPAutoTLS = true;
                }
                // Prevent Wordpress from overriding the SMTP FROM address (Office 365 compatibility)
                add_filter( 'wp_mail_from', function( $email ) {
                  return $_ENV["SMTP_FROM"];
                });

Of course, with a little tweaking you can apply the same setup in Docker, docker-compose and more, and at least in our case this eliminates the need for building a custom Docker image or running an MTA in a separate container.

Hope this helps,

does anyone know how to translate this into a docker-compose.yml? Thank you so much!

joeldeteves commented 3 years ago

does anyone know how to translate this into a docker-compose.yml? Thank you so much!

Hi @derspotter, you could try something like this (note the double $$ to prevent docker compose from interpreting environment variables in the PHP code):

(NOTE: I do not use Docker compose and I don't know the best way to handle storing sensitive info such as secrets. This is just a proof of concept showing how to translate a quick setup to a docker-compose file:

version: "3.9"
services:
  wordpress:
    image: wordpress:latest
    ports:
      - 8080:80
    environment:
      SMTP_HOSTNAME: smtp.office365.com
      SMTP_PORT: "587"
      SMTP_USER: email@xyz.com
      SMTP_PASSWORD: /run/secrets/smtp_password
      SMTP_FROM: email@xyz.com
      WORDPRESS_DB_HOST: wordpress-db
      WORDPRESS_DB_USER: wordpress
      WORDPRESS_DB_PASSWORD: /run/secrets/db_password
      WORDPRESS_DB_NAME: wordpress
      WORDPRESS_CONFIG_EXTRA: |
          // SMTP Settings
          require_once( ABSPATH .'wp-includes/plugin.php' );
          add_action( 'phpmailer_init', 'mail_smtp' );
          function mail_smtp( $$phpmailer ) {
              $$phpmailer->isSMTP();
              $$phpmailer->Host = getenv('SMTP_HOSTNAME');
              $$phpmailer->Port = getenv('SMTP_PORT');
              $$phpmailer->Username = getenv('SMTP_USER');
              $$phpmailer->Password = getenv('SMTP_PASSWORD');
              $$phpmailer->From = getenv('SMTP_FROM');
              $$phpmailer->FromName = getenv('SMTP_FROM_NAME');

              // Additional settings
              $$phpmailer->SMTPAuth = true;
              $$phpmailer->SMTPSecure = "tls";
              $$phpmailer->SMTPAutoTLS = true;

              // Filter out client message body and output debug info to the logs
              // NOTE: Log level must be set to '2' or higher in order for the filter to work
              $$phpmailer->SMTPDebug = 2;

              $$phpmailer->Debugoutput = function($$str) {
                  static $$logging = true;
                  if ($$logging === false && strpos($$str, 'SERVER -> CLIENT') !== false) {
                      $$logging = true;
                  }
                  if ($$logging) {
                      error_log("SMTP " . "$$str");
                  }
                  if (strpos($$str, 'SERVER -> CLIENT: 354') !== false) {
                      $$logging = false;
                  }
              };
          }
          // Prevent Wordpress from overriding the SMTP FROM address (Office 365 compatibility)
          add_filter( 'wp_mail_from', function( $$email ) {
              return $$_ENV["SMTP_FROM"];
          });
    volumes:
      - wp-data:/var/www/html
    secrets:
      - db_password
      - smtp_password
    depends_on:
      - wordpress-db

  wordpress-db:
    image: mariadb:latest
    environment:
      MYSQL_ROOT_PASSWORD: /run/secrets/db_root_password
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wordpress
      MYSQL_PASSWORD: /run/secrets/db_password
    volumes:
      - db-data:/var/lib/mysql
    secrets:
      - db_root_password
      - db_password

volumes:
  wp-data:
  db-data:

secrets:
  db_password:
    file: db_password.secret
  db_root_password:
    file: db_root_password.secret
  smtp_password:
    file: smtp_password.secret
derspotter commented 3 years ago

does anyone know how to translate this into a docker-compose.yml? Thank you so much!

Hi @derspotter, you could try something like this (note the double $$ to prevent docker compose from interpreting environment variables in the PHP code):

Thank you so much!!! it works :))

dont-panic-42 commented 3 years ago

Thanks @joeldeteves for the WORDPRESS_CONFIG_EXTRA + PHPMailer solution, it is working for me.

In case it saves someone else a few frustrating hours, it seems like the *-alpine Docker WP images are not able to do external networking (like connecting to remote external SMTP hosts). I'm talking about external connections to public internet hosts (like smtp.mailgun.org), not to your local Docker containers. Maybe that's common knowledge but it was news to me.

I am not sure what exactly is missing but from inside a container using the wordpress:5.6-php7.4-fpm-alpine image, I was not able to either resolve an IP for my mail host (smtp.mailgun.org), nor ping it. Switching to wordpress:5.6-php7.4-fpm solved the problem, though it took much hair pulling and gnashing of teeth to get there.

joeldeteves commented 3 years ago

it seems like the *-alpine Docker WP images are not able to do external networking (like connecting to remote external SMTP hosts)

I'm not able to reproduce this problem; I use alpine versions for all my deployments (currently running wordpress:5.6.2-php7.4-fpm-alpine).

You might want to double-check your config and/or firewall.

dont-panic-42 commented 3 years ago

I'm not able to reproduce this problem; I use alpine versions for all my deployments (currently running wordpress:5.6.2-php7.4-fpm-alpine).

@joeldeteves Interesting. I'm on macOS. It doesn't work for me. I'm not sure this is the right place for this tangent but maybe it is relevant and helps others. Super-simple docker-compose.yml demonstrating the problem:

version: '3'
services:
  wordpress1:
    image: wordpress:5.6-php7.4-fpm
  wordpress2:
    image: wordpress:5.6-php7.4-fpm-alpine
$ docker-compose up -d
Starting test_wordpress1_1 ... done
Starting test_wordpress2_1 ... done

$ docker exec -it test_wordpress1_1 bash
root@36939bbe8fcb:/var/www/html# curl -I https://github.com/docker-library/wordpress/issues/30
HTTP/2 200
server: GitHub.com
date: Sun, 14 Mar 2021 18:12:44 GMT
... lots of headers, clearly works fine
root@36939bbe8fcb:/var/www/html# exit

$ docker exec -it test_wordpress2_1 bash
bash-5.1# curl -I https://github.com/docker-library/wordpress/issues/30
curl: (6) Could not resolve host: github.com
joeldeteves commented 3 years ago

@joeldeteves Interesting. I'm on macOS. It doesn't work for me. I'm not sure this is the right place for this tangent but maybe it is relevant and helps others.

I still can't reproduce this (I used the compose example you provided above, both containers are able to resolve just fine) however I am running Ubuntu.

It is very probable that this is something specific to your environment, quite possibly Mac-OS specific.

You may want to run some more tests such as testing from a VM, but I do not believe it is related to the alpine image.

kimlehtinen commented 3 years ago

does anyone know how to translate this into a docker-compose.yml? Thank you so much!

Hi @derspotter, you could try something like this (note the double $$ to prevent docker compose from interpreting environment variables in the PHP code):

(NOTE: I do not use Docker compose and I don't know the best way to handle storing sensitive info such as secrets. This is just a proof of concept showing how to translate a quick setup to a docker-compose file:

version: "3.9"
services:
  wordpress:
    image: wordpress:latest
    ports:
      - 8080:80
    environment:
      SMTP_HOSTNAME: smtp.office365.com
      SMTP_PORT: "587"
      SMTP_USER: email@xyz.com
      SMTP_PASSWORD: /run/secrets/smtp_password
      SMTP_FROM: email@xyz.com
      WORDPRESS_DB_HOST: wordpress-db
      WORDPRESS_DB_USER: wordpress
      WORDPRESS_DB_PASSWORD: /run/secrets/db_password
      WORDPRESS_DB_NAME: wordpress
      WORDPRESS_CONFIG_EXTRA: |
          // SMTP Settings
          require_once( ABSPATH .'wp-includes/plugin.php' );
          add_action( 'phpmailer_init', 'mail_smtp' );
          function mail_smtp( $$phpmailer ) {
              $$phpmailer->isSMTP();
              $$phpmailer->Host = getenv('SMTP_HOSTNAME');
              $$phpmailer->Port = getenv('SMTP_PORT');
              $$phpmailer->Username = getenv('SMTP_USER');
              $$phpmailer->Password = getenv('SMTP_PASSWORD');
              $$phpmailer->From = getenv('SMTP_FROM');
              $$phpmailer->FromName = getenv('SMTP_FROM_NAME');

              // Additional settings
              $$phpmailer->SMTPAuth = true;
              $$phpmailer->SMTPSecure = "tls";
              $$phpmailer->SMTPAutoTLS = true;

              // Filter out client message body and output debug info to the logs
              // NOTE: Log level must be set to '2' or higher in order for the filter to work
              $$phpmailer->SMTPDebug = 2;

              $$phpmailer->Debugoutput = function($$str) {
                  static $$logging = true;
                  if ($$logging === false && strpos($$str, 'SERVER -> CLIENT') !== false) {
                      $$logging = true;
                  }
                  if ($$logging) {
                      error_log("SMTP " . "$$str");
                  }
                  if (strpos($$str, 'SERVER -> CLIENT: 354') !== false) {
                      $$logging = false;
                  }
              };
          }
          // Prevent Wordpress from overriding the SMTP FROM address (Office 365 compatibility)
          add_filter( 'wp_mail_from', function( $$email ) {
              return $$_ENV["SMTP_FROM"];
          });
    volumes:
      - wp-data:/var/www/html
    secrets:
      - db_password
      - smtp_password
    depends_on:
      - wordpress-db

  wordpress-db:
    image: mariadb:latest
    environment:
      MYSQL_ROOT_PASSWORD: /run/secrets/db_root_password
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wordpress
      MYSQL_PASSWORD: /run/secrets/db_password
    volumes:
      - db-data:/var/lib/mysql
    secrets:
      - db_root_password
      - db_password

volumes:
  wp-data:
  db-data:

secrets:
  db_password:
    file: db_password.secret
  db_root_password:
    file: db_root_password.secret
  smtp_password:
    file: smtp_password.secret

Thank you this worked with docker-compose! I created a separate gmail account that's only used to send mails on a wp-site of mine. If you use gmail as I did, you have to enable less secure apps in your gmail settings https://support.google.com/accounts/answer/6010255?hl=en

sceptic30 commented 3 years ago

Hello everyone. I figured out a clean and working solution without any hacks around WORDPRESS_CONFIG_EXTRA, or wordpress plugins. It was more of a php issue than wordpress it self. I work only with alpline images so the packages that needs to be installed are alpine packages. For extra security the php image will run under www-data. What i did was:

  1. install msmtp and mailx packages from edge/community repository
  2. create a soft link : ln -sf /usr/bin/msmtp /usr/sbin/sendmail
  3. Apply some permissions needed.
  4. echo 'sendmail_path = "/usr/bin/msmtp -t"' > /usr/local/etc/php/conf.d/msmtp.ini

The exact RUN statement is bellow:

RUN set -x \
    && echo "http://dl-cdn.alpinelinux.org/alpine/edge/community/" >> /etc/apk/repositories \
    && apk add --no-cache \
    msmtp \
    mailx \
    && ln -sf /usr/bin/msmtp /usr/sbin/sendmail \
    && chown 82:82 -R /var/mail \
    && touch /etc/msmtprc \
    && chown 82:82 /etc/msmtprc \
    && chmod 600 /etc/msmtprc \
    && echo 'sendmail_path = "/usr/bin/msmtp -t"' > /usr/local/etc/php/conf.d/msmtp.ini

The exact Php's Dockerfile is here https://github.com/sceptic30/php8-msmtp/blob/main/Dockerfile Based on that, i created a wordpress image like normally https://github.com/sceptic30/wordpress-php-fpm-redis-alpine/blob/master/Dockerfile

For those that they just want to download the images use these commands (currently the most up to date):

docker pull admintuts/php:7.4.18-fpm-alpine 
docker pull admintuts/php:8.0.5-fpm-alpine

And for Wordpress:

docker pull admintuts/wordpress:php7.4.18-fpm-redis-alpine
docker pull admintuts/wordpress:php8.0.5-fpm-redis-alpine

After that, follow instructions given here https://github.com/sceptic30/php8-msmtp#create-configuration-files-for-msmtp for the confuguration files that needs to be bind-mount to the container file system, and you should be good to go.

Losmoges commented 3 years ago

I have a running dovecot/postfix stack at arbitrary internal hostname dovecot.my for development. With the following php code loaded in wordpress the email functionality is working. Inspired by/copied from posts above.

require_once(ABSPATH .'wp-includes/plugin.php');

// configure phpmailer with SMTP before sending emails
add_action('phpmailer_init', function($mailer) {
  $mailer->isSMTP();
  $mailer->Host = "dovecot.my";
  $mailer->SMTPAuth = true;
  $mailer->Username = "wordpress@dovecot.my";
  $mailer->Password = "pass";
  $mailer->Port = 587;
});

// from_email defaults to 'wordpress@$sitename', which seems to be wordpress@localhost by default
// use this filter to make sure it matches your smtp host
add_filter('wp_mail_from', function($from) {
  return "wordpress@dovecot.my";
});

// optional: error logging in case phpmailer fails
add_action('wp_mail_failed', function($wperror) {
  error_log(implode("; ", $wperror->get_error_messages()));
});

I put the above in a WORDPRESS_CONFIG_EXTRA environment variable in my docker-compose.yml. Just be sure to duplicate all the $ signs so that docker-compose doesn't interpret them as bash/env variables. Note: any other way to add this script to your wordpress would probably work as well. No need to install any additional softwares in my wordpress container.

Just be aware that this is probably terrible security, don't use in production.

Tested on wordpress:5.7.2

CholoTook commented 1 year ago

If I add a plugin via the settings interface once the container is running, is that persisted? I'm using the example docker-compose.yml, and the only 'external' volume is /var/www/html so I suppose it SHOULDN@T be... However, a quick test with a restart shows that it is... I'm guessing it won't persist a rebuild? Not sure if this is an issue or not...

tianon commented 1 year ago

As long as you persist /var/www/html and your MySQL database, all your configuration (including content uploads like post attachments and installed plugins) will stay.

huksley commented 1 year ago

@Losmoges does not work, writes to log sh: 1: /usr/sbin/sendmail: not found Wordpress 6.1.1

jmhunter commented 1 year ago

If anyone's interested, my approach to solve this was as follows.

In docker-compose.yml:

wordpress:
  [...]
    volumes:
    - /data/docker/mysite/html:/var/www/html
    - /data/docker/mysite/msmtprc:/etc/msmtprc
    - /data/docker/mysite/msmtp-php.ini:/usr/local/etc/php/conf.d/msmtp-php.ini
  # Following is a method of installing msmtp without re-mastering the entire docker image
  #   (inspiration from https://stackoverflow.com/questions/47615751/docker-compose-run-a-script-after-container-has-started )
  # Entrypoint command taken from https://github.com/docker-library/wordpress/blob/master/Dockerfile.template
  command: sh -c "if [ ! -x /usr/bin/msmtp ]; then apt update; DEBIAN_FRONTEND=noninteractive apt -y install msmtp; fi; exec /usr/local/bin/docker-entrypoint.sh apache2-foreground"

msmtp-php.ini:

sendmail_path = "/usr/bin/msmtp -t -i"

msmtprc:

defaults

account mysite
host smtp.myhost.com
from me@mysite.com

account default: mysite

Seems to work in my very basic testing (I'm a complete Wordpress newbie; have only been looking at this to get a site up and running that somebody has asked me to host).

na0x2c6 commented 1 year ago

I noticed that I can send emails by wrapping docker-entrypoint.sh and starting the sendmail daemon. I hope this helps someone.

wrapped-entrypoint.sh

#!/bin/bash

# Start the sendmail daemon
/usr/sbin/sendmail -bd

# Execute the original entrypoint script
exec docker-entrypoint.sh "$@"

Dockerfile

FROM docker.io/library/wordpress:php7.4-apache

# Install sendmail
RUN apt-get update && apt-get install -y sendmail && apt-get clean

# Copy the wrapped entrypoint script
COPY wrapped-entrypoint.sh /usr/local/bin/

# Make the script executable
RUN chmod +x /usr/local/bin/wrapped-entrypoint.sh

# Use the wrapped entrypoint
ENTRYPOINT ["wrapped-entrypoint.sh"]
ja2ui0 commented 1 year ago

does anyone know how to translate this into a docker-compose.yml? Thank you so much!

Hi @derspotter, you could try something like this (note the double $$ to prevent docker compose from interpreting environment variables in the PHP code):

(NOTE: I do not use Docker compose and I don't know the best way to handle storing sensitive info such as secrets. This is just a proof of concept showing how to translate a quick setup to a docker-compose file:

version: "3.9"
services:
  wordpress:
    image: wordpress:latest
    ports:
      - 8080:80
    environment:
      SMTP_HOSTNAME: smtp.office365.com
      SMTP_PORT: "587"
      SMTP_USER: email@xyz.com
      SMTP_PASSWORD: /run/secrets/smtp_password
      SMTP_FROM: email@xyz.com
      WORDPRESS_DB_HOST: wordpress-db
      WORDPRESS_DB_USER: wordpress
      WORDPRESS_DB_PASSWORD: /run/secrets/db_password
      WORDPRESS_DB_NAME: wordpress
      WORDPRESS_CONFIG_EXTRA: |
          // SMTP Settings
          require_once( ABSPATH .'wp-includes/plugin.php' );
          add_action( 'phpmailer_init', 'mail_smtp' );
          function mail_smtp( $$phpmailer ) {
              $$phpmailer->isSMTP();
              $$phpmailer->Host = getenv('SMTP_HOSTNAME');
              $$phpmailer->Port = getenv('SMTP_PORT');
              $$phpmailer->Username = getenv('SMTP_USER');
              $$phpmailer->Password = getenv('SMTP_PASSWORD');
              $$phpmailer->From = getenv('SMTP_FROM');
              $$phpmailer->FromName = getenv('SMTP_FROM_NAME');

              // Additional settings
              $$phpmailer->SMTPAuth = true;
              $$phpmailer->SMTPSecure = "tls";
              $$phpmailer->SMTPAutoTLS = true;

              // Filter out client message body and output debug info to the logs
              // NOTE: Log level must be set to '2' or higher in order for the filter to work
              $$phpmailer->SMTPDebug = 2;

              $$phpmailer->Debugoutput = function($$str) {
                  static $$logging = true;
                  if ($$logging === false && strpos($$str, 'SERVER -> CLIENT') !== false) {
                      $$logging = true;
                  }
                  if ($$logging) {
                      error_log("SMTP " . "$$str");
                  }
                  if (strpos($$str, 'SERVER -> CLIENT: 354') !== false) {
                      $$logging = false;
                  }
              };
          }
          // Prevent Wordpress from overriding the SMTP FROM address (Office 365 compatibility)
          add_filter( 'wp_mail_from', function( $$email ) {
              return $$_ENV["SMTP_FROM"];
          });
    volumes:
      - wp-data:/var/www/html
    secrets:
      - db_password
      - smtp_password
    depends_on:
      - wordpress-db

  wordpress-db:
    image: mariadb:latest
    environment:
      MYSQL_ROOT_PASSWORD: /run/secrets/db_root_password
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wordpress
      MYSQL_PASSWORD: /run/secrets/db_password
    volumes:
      - db-data:/var/lib/mysql
    secrets:
      - db_root_password
      - db_password

volumes:
  wp-data:
  db-data:

secrets:
  db_password:
    file: db_password.secret
  db_root_password:
    file: db_root_password.secret
  smtp_password:
    file: smtp_password.secret

Thanks for this. It seems like this is the most straightforward solution that doesn't involve rebuilding the container, which is a huge plus. Still works perfectly.

punkyard commented 8 months ago

does anyone know how to translate this into a docker-compose.yml? Thank you so much!

Hi @derspotter, you could try something like this (note the double $$ to prevent docker compose from interpreting environment variables in the PHP code):

(NOTE: I do not use Docker compose and I don't know the best way to handle storing sensitive info such as secrets. This is just a proof of concept showing how to translate a quick setup to a docker-compose file:

hi, thanks a lot for your solution do you think you could detail the reason why using secrets and not writing the passwords directly in the compose.yml file as recomanded on WP official docker hub? I'm doing so at the moment in a portainer stack with volumes to /mnt folder.. is it risky according to you? ^^

LaurentGoderre commented 8 months ago

I think the main reason to avoid putting secrets in the docker-compose yaml is to prevent accidentally committing a real secret in source control.