Nodemailer + PlugSend SMTP (Port 587) — Copy/Paste Setup

Send transactional email from Node.js using Nodemailer and PlugSend SMTP on port 587. Includes production notes and debugging tips.

H

hb+test2@local.test

Mar 17, 2026 • 4 min read

If you’re sending password resets, OTPs, signup verification, invoices, or “your report is ready” notifications, email is part of your core product. And the fastest way to make it reliable is:

  • use SMTP on port 587 (STARTTLS)
  • treat sending as an async job (queues)
  • log the provider message-id so you can debug
  • set up SPF/DKIM/DMARC from day one

This guide focuses on Node.js + Nodemailer + PlugSend, but the workflow applies to any stack.

Quick answer

Use PlugSend SMTP with Nodemailer:

  • Host: from env (PLUGSEND_SMTP_HOST)
  • Port: 587
  • Encryption: STARTTLS (secure: false, Nodemailer will upgrade)
  • Username: SMTP user created for your verified domain (example: admin@yourdomain.com)
  • Password: API key

1) Create env vars (recommended)

Your SMTP host may change over time (load balancing, regions, etc.). Don’t hardcode it in code. Put it in env.

# PlugSend SMTP
PLUGSEND_SMTP_HOST=send1.plugsend.net
PLUGSEND_SMTP_PORT=587
PLUGSEND_SMTP_USERNAME=admin@yourdomain.com
PLUGSEND_SMTP_PASSWORD=YOUR_API_KEY
PLUGSEND_SMTP_ENCRYPTION=tls

# Your sender identity
MAIL_FROM_NAME="Acme"
MAIL_FROM_ADDRESS=no-reply@yourdomain.com

2) Install Nodemailer

npm i nodemailer

3) Create a reusable mailer module

A common mistake is creating a new transporter everywhere. Centralize it so you can tune timeouts, pooling, and logging.

import nodemailer from 'nodemailer';

export function createPlugSendTransporter() {
  const host = process.env.PLUGSEND_SMTP_HOST;
  const port = Number(process.env.PLUGSEND_SMTP_PORT || 587);

  if (!host) throw new Error('Missing PLUGSEND_SMTP_HOST');

  return nodemailer.createTransport({
    host,
    port,
    secure: false, // STARTTLS on 587
    auth: {
      user: process.env.PLUGSEND_SMTP_USERNAME,
      pass: process.env.PLUGSEND_SMTP_PASSWORD,
    },
    // Production-friendly timeouts
    connectionTimeout: 10_000,
    greetingTimeout: 10_000,
    socketTimeout: 20_000,
  });
}

4) Send an email and log the message-id

That messageId is your handle for debugging. Store it alongside your internal notification id.

import { createPlugSendTransporter } from './mailer.js';

const transporter = createPlugSendTransporter();

export async function sendVerifyEmail({ to, code }) {
  const info = await transporter.sendMail({
    from: `${process.env.MAIL_FROM_NAME} <${process.env.MAIL_FROM_ADDRESS}>`,
    to,
    subject: 'Verify your email',
    text: `Your code is: ${code}`,
  });

  // Important: log this, store it, index it.
  console.log('plugsend_message_id=', info.messageId);

  return info.messageId;
}

5) Where to look in PlugSend when something fails

PlugSend tracks a simple, useful event model:

  • submitted — accepted and queued
  • delivered — accepted by the recipient server
  • bounced — rejected (you’ll see the reason)

Workflow when you get a complaint like “your app didn’t email me”:

  1. Find the app log line with plugsend_message_id
  2. Search the PlugSend Logs by that message-id
  3. Check the timeline: submitted → delivered or submitted → bounced
  4. If bounced, fix the root cause (see troubleshooting below)

6) Troubleshooting: the failures you’ll actually see

Auth failures (535 / “authentication failed”)

  • Verify PLUGSEND_SMTP_PASSWORD is your API key (not a placeholder).
  • Verify username belongs to a verified domain and was created in PlugSend.
  • Make sure there are no extra spaces/newlines in env values (copy/paste issue).

Timeouts / hanging sends

  • Don’t send email inside your HTTP request path for user-facing endpoints. Queue it.
  • Check you’re using port 587, not 25 (many providers block 25).
  • Set timeouts (see transporter config above).

Emails go to spam (deliverability basics)

If you only do one thing, do this:

  • SPF record includes PlugSend
  • DKIM enabled (signing)
  • DMARC policy exists (p=none to start is fine)
  • Your From: domain matches your authenticated sending domain

Bounces

Don’t just retry blindly. Treat bounces as signals:

  • Hard bounces (mailbox doesn’t exist): stop sending to that address.
  • Soft bounces (temporary): retry with backoff.

7) Checklist (copy/paste)

  • [ ] Env vars set (host/port/user/api key)
  • [ ] Verified domain + SMTP user created for that domain
  • [ ] From address uses the same domain
  • [ ] SPF + DKIM + DMARC configured
  • [ ] App logs store PlugSend message-id
  • [ ] Sending is queued (not blocking user requests)

Next steps

If you want, I can add a “bounce handling” section that shows how to automatically suppress hard bounces and keep your sender reputation clean.

CTA: Create a free PlugSend account, verify your domain, create an SMTP user, generate an API key, and ship reliable email in minutes.

Related Articles

View all posts →