php: mail() does not work out of the box

It seems that the sendmail_from config setting isn’t right? If additional configuration is required for this to work should this be noted in the documentation?

When running php -i | grep sendmail_path I get this

php -i | grep sendmail_path
sendmail_path =>  -t -i  =>  -t -i 

When running this script

<?php 
mail('test@example.com', 'Subject', 'Body');
?>

The results are this:

sh: 1: -t: not found

About this issue

  • Original URL
  • State: closed
  • Created 9 years ago
  • Reactions: 15
  • Comments: 29 (3 by maintainers)

Most upvoted comments

I ended up trying to add sendmail or postfix to a container extending from php:5.6-apache and pointing the php.ini sendmail_path to them correctly but ran into the problem of multiple services running inside the container.

Ended up settling on using ssmtp to solve this. Here is a Dockerfile that should work with mail() out of the box.

FROM php:5.6-apache

RUN apt-get update && \
  apt-get install -y ssmtp && \
  apt-get clean && \
  echo "FromLineOverride=YES" >> /etc/ssmtp/ssmtp.conf && \
  echo 'sendmail_path = "/usr/sbin/ssmtp -t"' > /usr/local/etc/php/conf.d/mail.ini

mail() should work out of the box with this container. I’m not sure if this is something that should be modified and changed in the base php container or documented somewhere. Thoughts?

@stuartz-VernonCo thank you !

it took me wayyyy so much time to find out why it wasn’t working… it was not about the firewall, it was about what sendmail is expecting us to put in /etc/hosts

The last line of /etc/hosts has to contain this structure : [IP] "your.domain.com" [HOSTNAME]

So, once I changed my /etc/hosts to 127.0.0.1 noreply.domain.com e0dd810b0efd and restarted docker, it worked.

It’s sad that sendmail doesn’t give any clue about this.

# echo "127.0.0.1 noreply.domain.com $(hostname)" >> /etc/hosts

Here’s a simple way to enable PHP to send mail via a SMTP provider. Unless you want to manage your own sendmail service, and worry about the mail sending reputation of your host, etc, this is the way to go. You can replace the Sendgrid details in the example config with the SMTP provider of your choice (if you do, be sure to also update the port number, and note that some cloud providers block outbound port 25).

One caveat is that the script will block on the mail() call until the email has been accepted by the SMTP provider, it’s not async out of the box.

Dockerfile

FROM php:5.6-apache

RUN apt-get update && \
   apt-get install -y ssmtp && \
   apt-get clean

# SSMTP settings
COPY ssmtp.conf /etc/ssmtp/ssmtp.conf
# PHP mail settings
RUN echo 'sendmail_path = "/usr/sbin/ssmtp -t -i"' > /usr/local/etc/php/conf.d/mail.ini

ssmtp.conf

# Sendgrid setup
# https://sendgrid.com/docs/for-developers/sending-email/ssmtp/
mailhub=smtp.sendgrid.net:587
AuthUser=<username>
AuthPass=<password>
UseSTARTTLS=YES
# Allow the "From" email header.
FromLineOverride=YES

It took me way too much time to research and empliment. Hope this helps someone else.

for simple solution that may be filtered by spam filters: for more details, information used from: [http://www.tothenew.com/blog/setting-up-sendmail-inside-your-docker-container/]

Dockerfile bare-bones example: FROM php:apache ‘#’ debian:jessie, apache on port 80, php:latest ‘#’ retrieve needed system programs RUN apt-get update && apt-get upgrade -y sendmail && rm -rf /var/lib/apt/lists/* COPY ./app/ /var/www/html/ ‘#’ to be ran with docker exec later COPY ./config_files/mail_config.sh /var/www’ ‘#’ php:apache recommends using your own php.ini COPY ./config_files/php.ini /usr/local/etc/php/php.ini

.config_files/mail_config.sh example: line=$(head -n 1 /etc/hosts) line2=$(echo $line | awk ‘{print $2}’) echo “$line $line2.localdomain” >> /etc/hosts /etc/init.d/sendmail start sleep 1 service apache2 graceful

add to your ./config_files/php.ini: sendmail_path = /usr/sbin/sendmail -t -i

Then run the following to initiate it after starting container or put as a CMD in Dockerfile if yours doesn’t already have a CMD. docker exec container_name bash /var/www/mail_config.sh

EDIT May 16, 2019

added ability to send as desired domain (see replace MASQUERADE_AS)

previous EDIT Feb 10,2017

I had another program that was editing the /etc/hosts file at start up, and so I changed my mail_config.sh The change also made it possible to not have to edit /etc/mail/sendmail.mc to remove localhost access thanks to a hint from @charafsalmi.

** /mail_config.sh

#!/bin/sh #add host to /etc/hosts host=$(hostname) line=$(cat /etc/hosts |grep [1]27.0.0.1) #placed at end to prevent being changed by weave echo “$line noreply@yourdomain.com $host” >> /etc/hosts

#or if file is not being changed by other applications #sed -i -e “s/$line/$line noreply@yourdomain.com $host/g” /etc/hosts

#finally echo “$host” >> /etc/mail/relay-domains replace MASQUERADE_AS with your domain to have it send mail out as the desired domain line=$(cat /etc/mail/sendmail.mc | grep [M]ASQUERADE_AS) ‘s/^“$line”.*/MASQUERADE_AS(`YOURDOMAIN.COM’)dnl/’ /etc/mail/sendmail.mc m4 /etc/mail/sendmail.mc > /etc/mail/sendmail.cf #start service service sendmail restart # or sendmail -bd

I solved this problem with MSMTP, sendgrid (works for us, you could pick another service, obv) and some environment variables, which I believe is pretty lightweight. I stumbled across this thread and the answers here helped me, so here’s what I did for the next person:

DOCKERFILE

RUN apt-get update && apt-get install -y msmtp && apt-get clean && rm -r /var/lib/apt/lists/*
# configure sendmail
RUN { \
    	echo "php_admin_value[sendmail_path] = $(which msmtp) -ti "; \
    } >> /usr/local/etc/php-fpm.d/www.conf

# configure entrypoint
COPY fpm-entrypoint.sh /usr/local/bin
ENTRYPOINT ["fpm-entrypoint.sh"]

ENTRYPOINT

#!/bin/bash
set -euo pipefail

cat >> /etc/msmtprc <<-EOCFG
account         default
host         smtp.sendgrid.net
port         587
timeout         30
auth         on
tls          on
tls_starttls    on
tls_trust_file     /etc/ssl/certs/ca-certificates.crt
syslog          on

auto_from       off
user         ${MTA_SENDGRID_USER}
password        ${MTA_SENDGRID_PASS}
from         ${MTA_SENDGRID_FROM}
maildomain      ${MTA_SENDGRID_MAILDOMAIN}
EOCFG


exec "$@"

Well here is my simple solution:

Dockerfile (for your custom php image)

FROM php:fpm
RUN apt-get update && apt-get install -y sendmail
ADD ./php.sh /opt/php.sh
ADD sendmail.ini /usr/local/etc/php/conf.d/
RUN chmod u+x /opt/php.sh
WORKDIR /opt
CMD ["php.sh"]

sendmail.ini

# Enable sendmail
sendmail_path = "/usr/sbin/sendmail -t -i"

php.sh

#! /bin/sh
line=$(head -n 1 /etc/hosts)
line2=$(echo $line | awk '{print $2}')
echo "$line $line2.localdomain" >> /etc/hosts
/etc/init.d/sendmail start
php-fpm

Be sure to specify a hostname inside your docker-compose.yml or when running the container, e.g.:

docker run -it --hostname foobar acme/php

or

docker-compose.yml

version: '2'
services:
  phpfpm:
    image: acme/php
    container_name: "my_php_fpm"
    hostname: "foobar"

Thats it - php mail() will work now. It’s propably possible with alpine, too - but I did not manage to get that working yet. Feel free to enhance my solution 😉

In our setup we’ve a host with smtp configured to relay to mandrill. We want the containers to leverage this config. The simplest setup we’ve found is:

In the host

iptables rule

-A INPUT -s 172.17.0.0/16 -p tcp -m multiport --dports 25 -m comment --comment "120 Postfix connections from docker" -j ACCEPT

postfix:

main.cf:

-mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
+mynetworks = 127.0.0.0/8 172.17.0.0/16 [::ffff:127.0.0.0]/104 [::1]/128
+local_header_rewrite_clients = permit_inet_interfaces permit_mynetworks

master.cf:

-127.0.0.1:smtp      inet  n       -       n       -       -       smtpd
+smtp      inet  n       -       n       -       -       smtpd

In the Dockerfile

apt-get install msmtp

php.ini

sendmail_path="/usr/bin/msmtp --host 172.17.0.1 --port 25 --from=docker-`id -u` -ti" 

We add the user id in the --from for traceability. An alternative is to replace --from with --auto-from=on.


hope this helps someone.

The previous configs didn’t work for me due various reasons. Came to the next solution: Dockerfile

FROM php:apache
RUN apt-get update && \
  apt-get install -y exim4 &&\
  echo 'sendmail_path = "/usr/sbin/exim4 -t"' >> /usr/local/etc/php/conf.d/mail.ini && \
  echo 'SMTP = localhost' >> /usr/local/etc/php/conf.d/mail.ini && \
  echo 'smtp_port = 25' >> /usr/local/etc/php/conf.d/mail.ini 
COPY ./config/exim4.conf /etc/exim4/exim4.conf
RUN chmod 644 /etc/exim4/exim4.conf

exim4.conf

primary_hostname = anynameyouwant.io
hostlist legit_sending_hosts = 127.0.0.1 

local_interfaces = <; ::0 ; 0.0.0.0
ignore_bounce_errors_after = 2d
timeout_frozen_after = 7d
split_spool_directory = true
acl_smtp_rcpt = acl_check_rcpt
acl_smtp_data = acl_check_data

#                       ACL CONFIGURATION                            #
begin acl
acl_check_rcpt:
  accept  hosts = :
          control = dkim_disable_verify
  deny    message       = Restricted characters in address
          local_parts   = ^[./|] : ^.*[@%!] : ^.*/\\.\\./
  accept  hosts         = +legit_sending_hosts
          control       = submission
          control       = dkim_disable_verify
  accept  authenticated = *
          control       = submission
          control       = dkim_disable_verify
  deny    message       = "You are not allowed to send email"
acl_check_data:
  accept

#                      ROUTERS CONFIGURATION                         #
begin routers
dnslookup:
  driver = dnslookup
  transport = remote_smtp
  ignore_target_hosts = 0.0.0.0 : 127.0.0.0/8
  no_more

#                      TRANSPORTS CONFIGURATION                      #
begin transports
remote_smtp:
  driver = smtp

#                      RETRY CONFIGURATION                           #
begin retry
*                      *           F,2h,15m; G,16h,1h,1.5; F,4d,6h
#                   AUTHENTICATION CONFIGURATION                     #
begin authenticators

It’s based on rickw/debian-exim-send Hope it will help someone

I dunno guys. ssmtp didn’t work for me out of the box. I get ssmtp: Cannot open mailhub:25 errors in the log. It seems like it ignores connection settings set by php code (MantisBT in my case).

It wasn’t enough for me. I had to add

RUN sed -i 's@local@internet@' /etc/exim4/update-exim4.conf.conf \
  && update-exim4.conf

to be able to send emails to external addresses (like gmail).

But even after that, I am still getting errors in /var/log/exim4/mainlog

SMTP error from remote mail server after end of data: The IP you're using to send mail is not authorized to\n550-5.7.1 send email directly to our servers. 
Please use the SMTP relay at your\n550-5.7.1 service provider instead. 
Learn more at\n550 5.7.1  https://support.google.com/mail/?p=NotAuthorizedError

It’s probably not in the scope of this issue, since mail function is working, it’s more about sendmail configuration. But that’s something to be aware of.

The docs say this:

Where the sendmail program can be found, usually /usr/sbin/sendmail or /usr/lib/sendmail. configure does an honest attempt of locating this one for you and set a default, but if it fails, you can set it here.

Looks like configure is failing to set a value for sendmail_path since there is no sendmail binary installed when PHP is built.

Good points. Seems the most Docker-like way to do this would be to have two containers, the php one and a MTA one, have ssmtp setup in the php one linked to the MTA one. Not the easiest thing in the world to document in a general manner. I’ll close this for now since it doesn’t seem like there are any actions that can be taken. Thanks for the insight.

I don’t think it’s possible to have a reasonable default configuration for mail(). In the case of ssmtp, you need to point it at an SMTP server (likely providing credentials). Trying to use an actual MTA like Postfix or Sendmail means that multiple services will be running in the container, as you mention.

Given that, I think that documenting the issue and possible ways to fix it is the best that can be done.

I previously opened an issue for the wordpress image where users have mentioned other configurations they’ve used to get mail() working: https://github.com/docker-library/wordpress/issues/30