Pooled SMTP Connections
Using pooled SMTP connections keeps a fixed number of TCP/TLS connections open to the SMTP server and re‑uses them for every message. This dramatically reduces TLS hand‑shake latency and is perfect when either
- you need to blast out a large batch of e‑mails, or
- your provider caps the number of parallel connections you’re allowed to use.
Quick example
const nodemailer = require("nodemailer");
/**
* One shared transporter for your whole process.
* The transporter will automatically open up to `maxConnections`
* sockets and keep them warm.
*/
const transporter = nodemailer.createTransport({
host: "smtp.example.com",
port: 465,
secure: true,
pool: true, // ♻️ enable connection pooling
maxConnections: 5, // optional – defaults to 5
maxMessages: 100, // optional – defaults to 100
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS,
},
});
// Somewhere in your code – *do not* create a new transporter each time
await transporter.sendMail({
from: "Newsletters <noreply@example.com>",
to: "alice@example.com",
subject: "Hello pooled world",
text: "Hi Alice! 👋",
});
Transport options
| Option | Type | Default | Description |
|---|---|---|---|
pool | boolean | false | Enable connection pooling. |
maxConnections | number | 5 | Maximum simultaneous SMTP connections. |
maxMessages | number | 100 | How many messages to send per connection before it is recycled. |
Deprecated
The following options are deprecated and will be removed in a future major release:
rateDelta– size of the time window (ms) used for rate limiting (default:1000).rateLimit– how many messages may be sent during onerateDeltawindow. The limit is shared between all pooled connections.
Runtime helpers
transporter.isIdle() → boolean
Returns true if at least one connection slot is free.
transporter.close()
Closes all active connections immediately and drains the message queue. Idle connections are normally closed automatically after socketTimeout, so calling this manually is rarely required.
// Graceful shutdown
process.on("SIGTERM", async () => {
await transporter.close();
process.exit(0);
});
Event: idle
The transporter emits an idle event whenever a connection slot becomes available. This allows you to implement push‑style senders that pull messages from an external queue only when Nodemailer is ready for them:
const { getNextMessage } = require("./messageQueue");
transporter.on("idle", async () => {
while (transporter.isIdle()) {
const message = await getNextMessage();
if (!message) return; // queue is empty
try {
await transporter.sendMail(message);
} catch (err) {
console.error("❌ Failed to send", err);
}
}
});
Best practices
- Create one transporter and reuse it – every new
createTransport()call spawns its own pool. - Adjust
maxConnectionsandmaxMessagesto match the policy of your SMTP provider. - Handle back‑pressure using the
idleevent instead of pushing thousands of messages into memory. - Close the pool on graceful shutdown so that your process exits promptly.