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.
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 queueddelivered— accepted by the recipient serverbounced— rejected (you’ll see the reason)
Workflow when you get a complaint like “your app didn’t email me”:
- Find the app log line with
plugsend_message_id - Search the PlugSend Logs by that message-id
- Check the timeline:
submitted → deliveredorsubmitted → bounced - 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_PASSWORDis 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=noneto 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.