SMTP for Node.js — application mail that arrives
Node.js has no built-in mail function; almost every application uses Nodemailer for that. The library makes sending easy, but says nothing about deliverability: that depends entirely on the SMTP server you send through.
Nodemailer works with a transport. You create an SMTP transport with your relay's details — host, port, encryption and login — and the rest of your code stays untouched. What leaves through a local server or a test account today goes through 2mail tomorrow.
Why it goes wrong without a relay
A Node.js application typically sends mail at the moment it matters: a verification code, a password reset, a booking confirmation. Those are messages expected within seconds. If they leave from a server without reputation, some of them arrive late or not at all — and the user clicks "resend" until they give up.
Setting up SMTP in Node.js
Collect your details
Log in to your 2mail account and retrieve your SMTP server, username and password there.
Fill them in inside Node.js
Create an SMTP transport in Nodemailer. Set the host to 2mail's SMTP server and the port to 587, and switch the secure option off so the connection is upgraded via STARTTLS. Under auth, fill in the username and password of your 2mail login. Read those details from environment variables, not from the code. Nodemailer has a verify method to check the connection before you send the first real mail — use it.
Authenticate your domain
Make sure SPF, DKIM and DMARC are set correctly for your domain, so recipients can verify that the mail comes from you.
Test
From your platform, send a test message to an address at a major provider and check that it arrives in the inbox and not in the spam folder.
The fields Node.js asks for
Every platform names them slightly differently, but it is always the same five. The actual values are tied to your account and can be found in your 2mail account — take them from there, so you are sure to use the right ones.
| Field | What you enter |
|---|---|
| SMTP server (host) | The server name from your 2mail account. |
| Port | The port that matches your chosen encryption; your account shows which one. |
| Encryption | TLS. Never send unencrypted — your credentials would otherwise travel in plain text. |
| Username | The SMTP login of your 2mail account. |
| Password | The matching password. Never use the password of an employee's mailbox. |
SMTP configuration for Node.js in code
Working examples for port 587 with STARTTLS, ready to copy. Replace the sender address with an address on your own domain and check the server name and login in your 2mail account. The password belongs in an environment variable — never in the code and never in version control.
Nodemailer: createTransport for port 587
One transport, created at start-up and reused everywhere. The combination of secure: false and requireTLS: true is the right one for port 587: the connection starts in plain text, is upgraded to TLS through STARTTLS, and Nodemailer refuses to continue if that upgrade fails. Login and password come from environment variables.
// mailer.js
const nodemailer = require('nodemailer');
const transporter = nodemailer.createTransport({
host: 'relay.2mail.eu',
port: 587,
secure: false, // 587 starts plain and upgrades with STARTTLS; true is for port 465 only
requireTLS: true, // refuse to continue if the upgrade to TLS fails
auth: {
user: process.env.SMTP_USERNAME,
pass: process.env.SMTP_PASSWORD,
},
});
module.exports = transporter; Testing the connection with verify() and a first email
verify() runs through the complete sign-in without sending anything and throws an error with a code that tells you the cause — see the table of error messages further down. When sending for real, log the messageId and the server's response: that is your proof the relay accepted the message.
const transporter = require('./mailer');
async function main() {
// Checks DNS, port, TLS and login without sending anything.
await transporter.verify();
const info = await transporter.sendMail({
from: '"Example" <noreply@example.com>',
to: 'you@example.org',
subject: 'SMTP test',
text: 'Sent through the 2mail SMTP relay.',
});
console.log(info.messageId, info.response);
}
main().catch((err) => {
console.error(err.code, err.message);
process.exit(1);
}); Larger volumes: pooling connections
If your application sends in bursts — a round of reminders, a series of notifications — let Nodemailer keep a small number of connections open instead of setting up a new one per message. That is faster and does not look like abuse to the relay.
const transporter = nodemailer.createTransport({
pool: true, // keep connections open and reuse them
maxConnections: 3,
maxMessages: 100, // per connection, then reconnect
host: 'relay.2mail.eu',
port: 587,
secure: false,
requireTLS: true,
auth: {
user: process.env.SMTP_USERNAME,
pass: process.env.SMTP_PASSWORD,
},
}); Which emails Node.js sends
As soon as the relay is set up, all of these run through 2mail — you do not have to configure them one by one.
Pitfalls with Node.js
secure and port have to match
The secure option does not mean "safe or unsafe" but "TLS from the first byte". On port 587 secure should be off — STARTTLS follows afterwards. Switch it on anyway and the connection hangs or is dropped immediately, without a clear error.
One transport, reused
Create the transport once at start-up and reuse it, rather than setting up a new one per mail. During a spike — a reminder run, a campaign — you otherwise open hundreds of connections at once, which a relay may read as abuse. Nodemailer can pool connections; enable that for larger volumes.
Wait for the result and log it
Sending is asynchronous. If you do not wait for the server's response or do not log it, you never see that a message was refused. Log at least the message id and any rejection, and preferably hook up 2mail's webhooks for delivery and bounce, so you also see what goes wrong after handover.
Common error messages with Node.js
The messages you get when host, port, encryption or login are wrong — quoted literally, so you recognise them in your log, with the cause and the fix next to them.
| Message | Cause | Fix |
|---|---|---|
Error: connect ECONNREFUSED 127.0.0.1:587 | Nodemailer connects to your own machine instead of the relay. The host option is empty or undefined — typically an environment variable that does not exist in production — and Nodemailer then falls back to localhost. | Log the options just before createTransport and check that host really contains relay.2mail.eu. |
Error: Connection timeout | The relay does not answer (err.code is ETIMEDOUT). Outbound traffic on port 587 is being stopped by the firewall of your host, your container or your cloud platform. | Test from the same machine with openssl s_client -starttls smtp -connect relay.2mail.eu:587 and have outbound port 587 opened. |
Error: Invalid login: 535 5.7.8 Error: authentication failed | The relay rejects the login (err.code is EAUTH): a wrong username or password, or an environment variable that is empty, so that Nodemailer signs in with no login or an empty one. | Check SMTP_USERNAME and SMTP_PASSWORD in the environment the process runs in, and use the SMTP login from your 2mail account — not the password of a mailbox. |
Error: …:SSL routines:ssl3_get_record:wrong version number | secure: true on port 587. Nodemailer then starts with a TLS handshake right away, whereas the relay expects plain SMTP first on that port and only switches after STARTTLS. | Set secure: false and requireTLS: true. The connection is encrypted just as well. |
Error: self signed certificate | It is not the relay but something in between that presents the certificate: a corporate proxy or antivirus intercepting TLS, or a local test server that host still points to. | Fix the path rather than switching off the check. tls: { rejectUnauthorized: false } makes the error go away, but sends your login to whoever pretends to be the relay. |
A different platform?
SMTP is a standard protocol: any system that speaks SMTP — a CRM, an ERP, a custom application or a server — can send through 2mail using the same five fields.