How to Configure Multiple SMTP Messengers for Different Campaigns in Listmonk
To configure multiple SMTP messengers in Listmonk, define separate SMTP blocks in your configuration with unique name fields, then reference the specific server using the format email / <name> when creating campaigns.
Listmonk, the open-source newsletter and mailing list manager, treats every SMTP server definition as an independent messenger. When you need to route transactional emails through one provider and marketing campaigns through another, the system allows you to configure multiple SMTP endpoints and select the appropriate one per campaign. This architecture provides granular control over delivery infrastructure while maintaining a load-balanced fallback pool.
Understanding Listmonk's Messenger Architecture
Listmonk abstracts SMTP connectivity through a messenger system that creates two distinct endpoint types based on your configuration.
The Group Messenger (email)
When Listmonk initializes, it automatically creates a group messenger named email that encompasses all enabled SMTP servers. This messenger balances deliveries across every configured endpoint using a round-robin approach. If you do not specify a particular messenger when creating a campaign, Listmonk defaults to this pooled group.
Individual Named Messengers (email / <name>)
For each SMTP block that includes a non-empty name field, Listmonk registers a standalone messenger with the identifier email / <name>. For example, an SMTP server named Transactional becomes accessible as email / Transactional. These individual messengers bypass the load-balancing logic and route all emails for that campaign exclusively through the specified server.
Configuration and Registration
The messenger system is initialized during application startup by reading the smtp array from your settings.
Defining SMTP Servers in config.toml
In your config.toml (or via the Settings UI), define multiple [[smtp]] blocks. Each block must include a unique name to be registered as an individual messenger, as defined in the Settings.SMTP struct in models/settings.go (lines 83-101).
[[smtp]]
name = "Transactional"
enabled = true
host = "smtp.transactional.com"
port = 587
auth_protocol = "plain"
username = "tx_user"
password = "tx_pass"
tls_type = "starttls"
max_conns = 10
[[smtp]]
name = "Marketing"
enabled = true
host = "smtp.marketing.com"
port = 465
auth_protocol = "login"
username = "mk_user"
password = "mk_pass"
tls_type = "tls"
max_conns = 15
Startup Registration Process
During initialization, the initSMTPMessengers function in cmd/init.go (lines 64-99) performs the following sequence:
- Iterates over
ko.Slices("smtp")to unmarshal each server into anemail.Serverstruct. - Registers individual messengers for every server where
s.Nameis set. - Creates the combined group messenger
emailfrom the full server slice.
This registration populates the manager's messenger map, which internal/manager/pipe.go validates against during campaign creation (lines 28-31).
Creating Campaigns with Specific SMTP Messengers
Once configured, you can target specific SMTP servers at the campaign level via the API or UI by referencing the exact messenger ID.
Using the REST API
When creating a campaign via the REST API, set the messenger field to the specific server you want to use.
POST /api/campaigns HTTP/1.1
Content-Type: application/json
Authorization: Bearer <api_key>
{
"name": "Spring Sale",
"subject": "Exclusive Spring Discounts!",
"from_email": "newsletter@example.com",
"messenger": "email / Marketing",
"list_ids": [12, 34],
"content_type": "plain",
"body": "Hello {{.Subscriber.Name}}, enjoy our spring sale..."
}
This request routes the campaign exclusively through the smtp.marketing.com server defined in your configuration.
Fallback Behavior
If you specify a messenger that has been disabled or removed from the configuration, Listmonk implements fallback logic in cmd/campaigns.go (lines 706-712). When the manager attempts to look up the messenger ID (msg.Campaign.Messenger) and finds it unavailable, it automatically falls back to the default email group messenger, ensuring campaign delivery continues without manual intervention.
To explicitly use the pooled group and allow load balancing across all SMTP servers, set the messenger to the base identifier:
{
"name": "Weekly Digest",
"messenger": "email",
"list_ids": [1],
"body": "Weekly updates..."
}
Summary
- Listmonk's architecture treats each named SMTP server as an individual messenger (
email / <name>) while maintaining a default group messenger (email) for load balancing across all enabled servers. - Configuration requires adding SMTP blocks to
config.tomlwith uniquenamefields, as defined inmodels/settings.go. - Registration occurs at startup via
initSMTPMessengersincmd/init.go, which creates both individual and group messengers from the configuration slice. - Campaign targeting uses the
messengerfield in the API or UI to route emails through specific SMTP providers instead of the default pool. - Fallback protection in
cmd/campaigns.goensures campaigns sent to invalid or disabled messengers default to theemailgroup, preventing delivery failures.
Frequently Asked Questions
What happens if I don't specify a name for an SMTP server?
If an SMTP block omits the name field, Listmonk does not register it as an individual messenger. The server is only included in the default email group messenger for load-balanced delivery. According to the source code in cmd/init.go, the individual messenger registration only triggers when s.Name is non-empty.
How does Listmonk handle load balancing across SMTP servers?
The default email messenger acts as a round-robin load balancer across all enabled SMTP servers defined in your configuration. When you use email as the messenger (instead of email / <name>), the campaign manager distributes messages across every configured endpoint automatically.
Can I switch messengers after a campaign has started?
No, the messenger is locked when the campaign is created and validated against the registered messengers in internal/manager/pipe.go. Changing the SMTP configuration after a campaign is active does not affect already-queued messages. However, if the specific SMTP server becomes unavailable during sending, the fallback logic in cmd/campaigns.go redirects to the group email messenger rather than failing entirely.
Where is the messenger validation logic implemented?
Campaign messenger validation occurs in internal/manager/pipe.go (lines 28-31), where the code checks that msg.Campaign.Messenger exists in the manager's m.messengers map. The messenger registration itself happens in cmd/init.go via the initSMTPMessengers function, while the actual SMTP implementation resides in internal/messenger/email/email.go.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →