【Docker】Apache + PHP-FPM コンテナからサーバー上のPostfixを利用してメールを送信する

こんにちは

今回は表題のとおり サーバー上に直接ホストしている Apache + PHP-FPM コンテナから、同一サーバー上に直接ホストしているPostfixを利用して別サーバーへメールを送信してみました。

方法としては、デフォルトではPHP-FPMコンテナ上からサーバー上のPostfixへ送信するための設定が難しいため、msmtp を利用して実装しました。

なお、やってみた背景としては、純粋にPHPコンテナからサーバー上のPostfixに対してどのようにしてメールを送信するのか疑問に思ったからとなります。

それでは、やっていきましょう

 

※注意点

・基本的なメール送信とDockerの技術については説明しません

→ 今回は Dovecotを利用せずに、Postfixを利用したMTA間の転送でメールの受信を確認します

 

▼前提

環境:vagrant(プライベート環境)

OS:AlmaLinux9

 

▼構成と送信流れ

サーバー1:Apache + PHP-FPMをコンテナでホスト、Postfixでメール送信、ドメイン名は dockermail01.mailtest.com

サーバー2:Postfixでメール受信、ドメイン名は mailclient01.mailtest.com

 

ブラウザ上からアクセス

サーバー1のApacheコンテナでリクエスト受信

サーバー1のPHP-FPMコンテナへローカルプロキシ

ブラウザ上から宛先などの情報を入力画面を表示し、情報を入力後、送信ボタンを押下

サーバー1のPostfix へメール送信リクエストを送信

サーバー2のPostfix へメール配送

 

▼手順

1. サーバー上でPostfixのセットアップ

まずは、サーバー1と2でPostfixをセットアップします

なお、具体的な手順はこちらの記事を参考にして進めますが、下記2点は修正してください

・ドメイン名を example.com 以外にする

→ メール送信の際にNull MX のエラーとなるため

 

・main.cf へ ignore_mx_lookup_error = yes を追記する

→ smtp_host_lookup = native を追記しており、DNS名前解決よりも/etc/hosts 設定が優先されるはずですが、なぜか私の環境ではDNS名前解決が優先されたため

 

また、今回はローカルでメールの送受信をするため、/etc/hosts は下記を追記しました

192.168.33.95 dockermail01.mailtest.com
192.168.33.96 mailclient01.mailtest.com

 

2. サーバー1でDockerとdocker-compose のセットアップ

次はApache + PHP-FPMコンテナをホストするために、サーバー1で Docker をセットアップします

なお、具体的な手順は以前書いたこちらの記事を参考にしてください

 

3. Apache + PHP-FPM コンテナの起動

各Dockerファイルを準備します

 

・全体の構成

.
├── app
│   ├── Dockerfile
│   ├── conf
│   │   ├── disable_display_error.ini
│   │   ├── msmtprc
│   │   ├── php.ini
│   │   └── zz-docker.conf
│   └── src
│       ├── index.php
│       ├── mail_form.php
│       └── test_mail.php
├── docker-compose.yml
└── web
    ├── Dockerfile
    └── conf
        ├── dockermail01.mailtest.com.conf
        └── httpd.conf

 

・docker-compose.yml

version: "3.9"
services:
  web01:
    build:
      context: ./
      dockerfile: web/Dockerfile
    volumes:
      - ./app/src:/var/www/vhosts/dockermail01.mailtest.com/public_html
    ports:
      - "80:80"

  app01:
    build:
      context: ./
      dockerfile: app/Dockerfile
    volumes:
      - ./app/src:/var/www/vhosts/dockermail01.mailtest.com/public_html
      - ./app/conf/msmtprc:/etc/msmtprc
    restart: always
    depends_on:
      - web01
    extra_hosts:
      - "dockermail01.mailtest.com:host-gateway"

 

・app/Dockerfile

FROM php:8.0.30-fpm

#ブラウザ上にphpのエラー内容を出力させない場合は下記を有効化する
COPY app/conf/disable_display_error.ini $PHP_INI_DIR/conf.d/

#php.iniの設置
COPY app/conf/php.ini $PHP_INI_DIR/conf.d/php.ini

#php-fpm用confファイルコピー
COPY app/conf/zz-docker.conf /usr/local/etc/php-fpm.d/

#msmtp用confファイルの設置
COPY app/conf/msmtprc /etc/msmtprc

#コンテナ実行ユーザーとグループをrootから変更
RUN groupmod -g 1000 www-data
RUN usermod -u 1000 www-data

#ドキュメントルート作成
# DEBIAN_FRONTEND=noninteractiveがないとmsmtpがインタラクティブで立ち上がってしまいイメージビルドが進まなくなる
RUN apt-get update -y && \
    DEBIAN_FRONTEND=noninteractive apt-get install -y vim less git msmtp msmtp-mta && \
    docker-php-ext-install pdo_mysql && \
    mkdir -p /var/www/vhosts/dockermail01.mailtest.com/public_html

COPY ./app/src /var/www/vhosts/dockermail01.mailtest.com/public_html

#ドキュメントルートの権限変更
RUN chown -R www-data:www-data /var/www/vhosts/dockermail01.mailtest.com/public_html

#ログファイルの権限変更
RUN chown -R www-data:www-data /var/log/

# ユーザー切り替え
USER www-data

#作業ディレクトリ設定
WORKDIR /var/www/vhosts/dockermail01.mailtest.com/public_html

#ポート開放
EXPOSE 9000

→ 注意点として、DEBIAN_FRONTEND=noninteractive を指定しないと、msmtpが入力待ちとなり、イメージビルドが止まります

 

・app/conf/disable_display_error.ini

display_errors = Off
log_errors = true

→ PHPのエラーログを出力させます

 

・app/conf/msmtprc

defaults
auth off
tls off
logfile /var/log/msmtp.log

account default
host dockermail01.mailtest.com
port 25
from user01@dockermail01.mailtest.com

→ msmtp の設定ファイルです

 

・app/conf/php.ini

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

→ 基本的にはデフォルトでOKですが、下記のみ msmtp ように修正が必要です

 

・app/conf/zz-docker.conf

[global]
daemonize = no

[www]
listen = 9000
pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 35

→ php-fpmのチューニング用ファイルです(なくても大丈夫ですが、その場合はDockerfileを修正してください

 

・app/src/index.php

<html>
        <head>
                <meta charset="utf-8" />
        </head>
        <body>
          <h2>This is an index page</h2>
          <a href="./mail_form.php">メール送信フォーム</a>
        </body>
</html>

→ indexファイルとなり、ブラウザから最初にアクセスした際に表示されます

 

・app/src/mail_form.php

<html>
        <head>
                <meta charset="utf-8" />
        </head>
        <body>
                <form action="./test_mail.php" method="post">
                    <p>送り先</p><input type="text" name="to">
                    <p>件名</p><input type="text" name="title">
                    <p>メッセージ</p><textarea name="content" cols="60" rows="10"></textarea>
                    <p><input type="submit" name="send" value="送信"></p>
                </form>
                <a href="./index.php">戻る</a>
        </body>
</html>

→ メールを送信するために必要な情報を入力するための画面です

 

・app/src/test_mail.php

<?php

  //変数の初期化
  $to = $title = $content = $headers = '';

  mb_language("Japanese");
  mb_internal_encoding("UTF-8");

  $to = $_POST['to'];
  $title = $_POST['title'];
  $content = $_POST['content'];
  $from = "user01@dockermail01.mailtest.com";
  $header = 'From: ' . $from ;

  if(mb_send_mail($to, $title, $content, $header, '-f ' . $from))
    {
      echo "メール送信成功です";
    } else {
      echo "メール送信失敗です";
    }
?>

→ メールを送信処理を実施するプログラムとなり、ブラウザ上から受け取ったPOSTの中身を渡しています

 

・web/Dockerfile

#ローカルでコンバートしたイメージを指定
FROM httpd:latest

#デフォルトのconfファイルでは下記パスのみ設定されているので、下記以外のパスでドキュメントルートを作成する場合は <Directory> で別途ドキュメントルートパスを指定する必要あり
#<Directory "/usr/local/apache2/htdocs">

#デフォルトで下記includeはコメントアウトされているので、デフォルトのconfを書き換える必要がある
# Virtual hosts
#Include conf/extra/httpd-vhosts.conf

#デフォルトのconfファイルを上書き
COPY web/conf/httpd.conf /usr/local/apache2/conf/httpd.conf

#追加のvhost用confファイルをコピー
COPY web/conf/dockermail01.mailtest.com.conf /usr/local/apache2/conf/extra

#コンテナ実行ユーザーとグループをrootから変更
RUN groupmod -g 1000 www-data
RUN usermod -u 1000 www-data

RUN apt-get update -y && apt-get install less

#ドキュメントルート作成
RUN mkdir -p /var/www/vhosts/dockermail01.mailtest.com/public_html

#appコンテナ配下のソースコードをコピー
COPY ./app/src /var/www/vhosts/dockermail01.mailtest.com/public_html

#ドキュメントルートとPIDの権限変更
RUN chown -R www-data:www-data /var/www/vhosts/dockermail01.mailtest.com/public_html && \
    chown -R www-data:www-data /usr/local/apache2/logs

# ユーザー切り替え
USER www-data

#作業ディレクトリ設定
WORKDIR /var/www/vhosts/dockermail01.mailtest.com/public_html

#ポート開放
EXPOSE 80

 

・web/conf/httpd.conf

→ 基本的には特に触ることはないですが、何か追加モジュールがある場合ように設定してます(なくても大丈夫ですが、その場合はDockerファイルを修正してください

 

・web/conf/dockermail01.mailtest.com.conf

<VirtualHost *:80>
  ServerAdmin test@example.com
  ServerName dockermail01.mailtest.com
  DocumentRoot /var/www/vhosts/dockermail01.mailtest.com/public_html
  <Directory /var/www/vhosts/dockermail01.mailtest.com/public_html>
    Options FollowSymLinks
    AllowOverride All
  </Directory>
  ProxyPassMatch "^/(.*\.php(/.*)?)$" "fcgi://app01:9000/var/www/vhosts/dockermail01.mailtest.com/public_html/$1"
   CustomLog "logs/dockermail01.mailtest.com-access_log" combined
   ErrorLog "logs/dockermail01.mailtest.com-error_log"
</VirtualHost>

→ dockermail01.mailtest.com でホストし、.php のリクエストはすべて app01サービス(PHP-FPMコンテナ)へローカルプロキシしています

 

各種ファイルの作成が完了したら、docker-compose.yml ファイルを配置しているディレクトリ上でイメージビルドとコンテナ立ち上げを実施します

イメージビルド

docker-compose build --no-cache

 

コンテナ起動

docker-compose up -d

 

起動確認

docker container ps -a

→ 下記のように表示されればOKです

CONTAINER ID   IMAGE          COMMAND                  CREATED        STATUS       PORTS                                 NAMES
32cf481c8bb6   docker-app01   "docker-php-entrypoi…"   20 hours ago   Up 2 hours   9000/tcp                              docker-app01-1
7dc63cc887fa   docker-web01   "httpd-foreground"       20 hours ago   Up 2 hours   0.0.0.0:80->80/tcp, [::]:80->80/tcp   docker-web01-1

 

4. ブラウザから表示確認

今回はローカル環境での確認となるので、/etc/hosts ファイル上でローカル名前解決をします

{サーバー1のIPアドレス} dockermail01.mailtest.com

 

それでは、http://dockermail01.mailtest.com へアクセスし、コンテンツが表示されていればOKです

ただ、この状態だとまだメールは送信できないので、以降の設定をします

 

5.  msmtprc の説明

まずは今回の肝となる、msmtprc の説明をします

msmtprc はmsmtpの設定ファイルとなり、コンテナ内へ /etc/msmtprc として配置することで設定が有効なります

defaults
auth off
tls off
logfile /var/log/msmtp.log

account default
host dockermail01.mailtest.com
port 25
from user01@dockermail01.mailtest.com

→ 重要な部分のみ説明すると、host はsmtpサーバーを指定するので、今回の場合とメール送信サーバーであるサーバー1(/etc/hostsで設定した名前)を指定します

また、from はメール送信元アカウントを設定しているので、サーバー1内に作成している ユーザー名@dockermail01.mailtest.com を指定しています

 

6. Postfix の main.cf でDockerネットワークのIPを許可する

今回の構成だとメール送信経路は、サーバー1のPHP-FPMコンテナ → サーバー1上の Postfix となるので、 Postfix の mynetworks でDockerのネットワークを送信元として許可する必要があります

まずは、PHP-FPMコンテナのネットワークを確認します

docker inspect ls {コンテナID} | less
"Networks": {
                "docker_default": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": [
                        "docker-app01-1",
                        "app01"
                    ],
                    "DriverOpts": null,
                    "GwPriority": 0,
                    "NetworkID": "d501262d819f04fc3b69a756da52cf2733ad8932ab6c710da16ec6caa60d4126",
                    "EndpointID": "2576460a524355db2032b70134ea017ab51621553097faf89a070c7d3e28a14c",
                    "Gateway": "172.18.0.1",
                    "IPAddress": "172.18.0.2",
                    "MacAddress": "e2:50:48:8c:84:de",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "DNSNames": [
                        "docker-app01-1",
                        "app01",
                        "32cf481c8bb6"
                    ]
                }
            }
        },

→ 上記の場合だと、「”IPAddress”: “172.18.0.2”」であり、「”IPPrefixLen”: 16」 となっているので、許可するネットワーク帯は「172.18.0.0/16」になります

 

サーバー1の main.cf で先ほどのネットワーク帯を記載します

vi /etc/postfix/main.cf
mynetworks = 192.168.33.0/24, 127.0.0.0/8, 172.18.0.0/16

 

postfixを再起動します

systemctl restart postfix

 

7. ブラウザからメールを送信してみる

http://dockermail01.mailtest.com/mail_form.php へアクセスし、下記事項を埋めてメールを送信します

送り先:user02@mailclient01.mailtest.com

件名:test

メッセージ:test

→ 送り先にはサーバー2のpostfixで設定しているホスト名とユーザー名を指定します

 

8. サーバー2でメール受信確認

サーバー2の user02 でメールを受信していることを確認します

[user02@mailclient01 new]$ ls -la /home/user02/Maildir/new
total 44
drwx------. 2 user02 user02 4096 Mar 14 07:05 .
drwxr-xr-x. 5 user02 user02   39 Mar  7 09:10 ..
-rw-------. 1 user02 user02  797 Mar 14 07:05 1773471912.V804I3044bdbM886760.mailclient01
[user02@mailclient01 new]$ cat 1773471912.V804I3044bdbM886760.mailclient01
Return-Path: <user01@dockermail01.mailtest.com>
X-Original-To: user02@mailclient01.mailtest.com
Delivered-To: user02@mailclient01.mailtest.com
Received: from dockermail01.mailtest.com (dockermail01.mailtest.com [192.168.33.95])
        by mailclient01.mailtest.com (Postfix) with ESMTPS id BF7E83E5BE
        for <user02@mailclient01.mailtest.com>; Sat, 14 Mar 2026 07:05:12 +0000 (UTC)
Received: from localhost (unknown [172.18.0.2])
        by dockermail01.mailtest.com (Postfix) with ESMTP id B40592021DE5
        for <user02@mailclient01.mailtest.com>; Sat, 14 Mar 2026 07:05:01 +0000 (UTC)
Date: Sat, 14 Mar 2026 07:05:01 +0000
To: user02@mailclient01.mailtest.com
Subject: test
From: user01@dockermail01.mailtest.com
MIME-Version: 1.0
Content-Type: text/plain; charset=ISO-2022-JP
Content-Transfer-Encoding: 7bit

test

→ きちんと受信できていればOKです

 

▼まとめ

いかかでしたでしょうか?

あまり本番環境でコンテナをホストすることはないかもしれませんが、メール送信の新しい知見として知れてよかったです。

ただ、まだまだメール送信技術は深いので引き続き勉強していきたいと思います。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA