5

I am trying to set up a docker-mailserver instance to use the nodemailer module to send emails from in my local dev setup. Here's what my docker-compose.yml file looks like:

version: '3.1'

services:
  postgres:
    container_name: postgres
    image: postgres
    restart: always
    volumes:
      - $PWD/.dbData:/data/db
    networks:
      - skynet
    environment:
      POSTGRES_PASSWORD: <PASSWORD>
      POSTGRES_USER: <USER>
      POSTGRES_DB: <DB>
  mailserver:
    image: docker.io/mailserver/docker-mailserver:latest
    env_file: $PWD/mailserver.env
    hostname: mailserver
    domainname: example.com
    container_name: mailserver
    ports:
      - "25:25"
      - "143:143"
      - "587:587"
      - "993:993"
    volumes:
      - $PWD/.mailData/maildata:/var/mail
      - $PWD/.mailData/mailstate:/var/mail-state
      - $PWD/.mailData/maillogs:/var/log/mail
      - /etc/localtime:/etc/localtime:ro
      - ./config/:/tmp/docker-mailserver/
    environment:
      - ENABLE_SPAMASSASSIN=1
      - SPAMASSASSIN_SPAM_TO_INBOX=1
      - ENABLE_CLAMAV=1
      - ENABLE_FAIL2BAN=1
      - ENABLE_POSTGREY=1
      - ENABLE_SASLAUTHD=0
      - ONE_DIR=1
      - DMS_DEBUG=0
    cap_add:
      - NET_ADMIN
      - SYS_PTRACE
    restart: always
    networks: 
      - skynet
  adminer:
    image: adminer
    hostname: adminer
    ports:
      - '8080:8080'
    depends_on:
      - postgres
    networks:
      - skynet
  app:
    # ... app details
networks:
  skynet:

As per the docs, I copied the example mailserver.env file and setup.sh administrative script. When I try to run docker-compose up -d, I see the following in the docker logs for the mailserver container:

Jun 22 23:25:53 mailserver postfix/master[26133]: fatal: bind: private/proxywrite: Invalid argument

I think this might be causing an issue, because when I try to send a simple message with nodemailer (on the same machine), like so:

const nodemailer = require('nodemailer')

let transporter = nodemailer.createTransport({
  host: 'localhost', // <--- defaults to this anyway
  port: 587,
})

const message = {
  from: 'foo@example.com',
  to: '<MY_PERSONAL_EMAIL>@gmail.com',
  subject: 'YOUR CARS EXTENDED WARRANTY',
  text: 'WEVE BEEN TRYING TO REACH YOU ABOUT YOUR CARS EXTENDED WARRANTY',
  html: '<p>WEVE BEEN TRYING TO REACH YOU ABOUT YOUR CARS EXTENDED WARRANTY</p>',
}

transporter.sendMail(message).then(
  () => {
    console.log('success!')
  },
  (err) => {
    console.error('=========== error occurred', err)
  },
)

I get the following error:

error occurred Error: Unexpected socket close
    at Timeout._onTimeout (/path/to/nodemailer-test/node_modules/nodemailer/lib/smtp-transport/index.js:189:31)
    at listOnTimeout (internal/timers.js:551:17)
    at processTimers (internal/timers.js:494:7)

Any help would be greatly appreciated.

Louis Kim
  • 51
  • 2
  • Cannot reproduce it :/ - you are using Linux though right, not Mac/Windows? – Tobias K. Jun 25 '21 at 08:37
  • Also, are you certain this is the right solution ? To send mails from a local Dev-Box to external addresses has more challenges, most servers will reject your mails if they don't come from a machine with valid public Domain&Helo&RDNS aswell as SPF-validated Return-Path. I don't know your use-case ofc, but maybe forwarding to a Mailhub that handles SMTP or using Mailhog is better? – Tobias K. Jun 25 '21 at 08:40
  • 1
    Can you connect to postfix via `telnet localhost 587` ? What happens if you connect via your host IP instead of localhost? – Stephan Pieterse Jun 25 '21 at 19:04
  • * Is it nodemailer running on a container? * Have you checked if have any other software using any of those port? `ss -lntp`? * What's you environment, linux, mac, etc..? – pbacterio Jun 30 '21 at 10:08

1 Answers1

0

I guess you are running your node code in the app container.

You were trying to talk to localhost:587 from there which would be your app container's port 587, not the one available on the service mailserver (which you also bound to your host machine).

You might have wanted to talk to the host there, but getting the host machine IP in docker is not trivial. Fortunately it is also not needed in this case: Since both containers are part of the same docker-compose project, they are automatically routed internally by the service name.

This would let your app container talk to the mailserver container directly:

let transporter = nodemailer.createTransport({
  host: 'mailserver',
  port: 587,
})

Some more troubleshooting:

  • mailserver is only part of the skynet network. Make sure that app does belong to that, too. netcat/telnet/ping can help you troubleshoot: docker-compose exec -it app bash - then inside the container: ping mailserver
  • Compare your mailserver service configuration to https://github.com/docker-mailserver/docker-mailserver/blob/v10.0.0/docker-compose.yml - it seems you used an older configuration version but mailserver/docker-mailserver:latest refers to v10. It seems their example configuration from README.md/docker hub page does not reflect that yet. Since it is the first release of v10, you might want to try with mailserver/docker-mailserver:9 (it is good practice anyways to "pin" the major version of used dependencies)
  • Try to send your mail via the other ports. I am using STARTTLS on port 25 with docker-mailserver for example to send mails from my apps.
Jonas Eberle
  • 2,835
  • 1
  • 15
  • 25