Configuring Sendmail for Laravel
Overview
Sendmail is a classic Mail Transfer Agent (MTA) with a long history in Unix-based systems. While modern alternatives like Postfix and Exim dominate new deployments, Sendmail remains valuable for legacy systems, deep customization, and specific integration scenarios.
This guide is not a recommendation to use Sendmail everywhere, but rather a comprehensive reference for developers who need to configure it—whether working with legacy systems, complex requirements, or custom mail handling.
When to Use Sendmail
✅ Good use cases:
- Integrating with existing Sendmail infrastructure
- Advanced customization and local mail handling
- Legacy systems requiring Sendmail compatibility
- Direct SMTP control without external services
❌ Not recommended for:
- New deployments (use Postfix or Exim instead)
- Simple hosting setups (use managed SMTP services)
- Beginners (Postfix is much simpler)
System Requirements
Before installing Sendmail, ensure:
| Component | Requirement | Notes |
|---|---|---|
| OS | Linux, BSD, macOS | Unix-based systems only |
| RAM | 256 MB minimum | 512 MB+ recommended |
| Disk | 1 GB minimum | For queue and logs |
| m4 macro processor | Required | sudo apt-get install m4 |
| PHP | 5.2+ | For proc_open() function |
Key Limitation
Important: Laravel's Sendmail transport uses proc_open(), a PHP function that must be enabled. If disabled in php.ini (common in shared hosting), you cannot use Sendmail with Laravel.
Installation
Install Sendmail
Ubuntu/Debian:
sudo apt-get update
sudo apt-get install sendmail sendmail-bin m4
sudo systemctl enable sendmail
sudo systemctl start sendmail
CentOS/RHEL:
sudo yum install sendmail sendmail-cf m4
sudo systemctl enable sendmail
sudo systemctl start sendmail
Verify installation:
sendmail -v
Understanding Sendmail Configuration
.mc vs .cf Files
Sendmail's configuration system is unique and often confusing:
| File Type | Purpose | Editable? |
|---|---|---|
.mc file |
Human-readable configuration | ✅ Yes (edit this) |
.cf file |
Compiled by m4; used by Sendmail | ❌ No (don't edit directly) |
The workflow:
.mc file → m4 processor → .cf file → Sendmail reads .cf
The m4 Macro Processor
Sendmail uses m4 to compile .mc files into .cf files. Key concepts:
- Processes as stream — Order of directives matters
dnlcommand — "Delete through newline"; used for comments- Macro expansion — Happens even in comments; can cause surprises
Convert .mc to .cf:
m4 /etc/mail/cf/m4/cf.m4 sendmail.mc > sendmail.cf
Basic .mc File Structure
A proper Sendmail .mc file follows this order:
# Version control
VERSIONID(`$Id: example.mc,v 1.0 2024/01/01 $')dnl
# Operating system (REQUIRED)
OSTYPE(`linux')dnl
# Domain configuration (optional but recommended)
DOMAIN(`generic')dnl
# Feature definitions (must come before MAILER)
FEATURE(`use_cw_file')dnl
FEATURE(`mailertable')dnl
FEATURE(`virtusertable')dnl
# Macro definitions (depends on FEATURE)
define(`SMART_HOST', `smtp.example.com')dnl
define(`MAIL_HUB', `mail.example.com')dnl
# Local customizations
LOCAL_CONFIG
# Custom rules here
# Mailer definitions (MUST BE LAST)
MAILER(`local')dnl
MAILER(`smtp')dnl
Critical rule: Place MAILER() directives last. Features that depend on macros must come before those macros are used.
Essential Configuration Directives
OSTYPE (Required)
Specifies the operating system. This is mandatory and controls paths, flags, and defaults.
OSTYPE(`linux')dnl
Common values:
linux— Linux systemsbsd44— BSD variantssolaris— Solaris/SunOShpux— HP-UXaix— IBM AIX
Without OSTYPE, the build will fail.
DOMAIN (Optional but Recommended)
Sets domain-specific configurations like local names and masquerading:
DOMAIN(`generic')dnl
Or custom domain (create /etc/mail/domain/example.com.m4):
DOMAIN(`example.com')dnl
MAILER (Required)
Defines which mailers Sendmail uses. local is included by default, but you must explicitly include smtp for outgoing mail:
MAILER(`local')dnl
MAILER(`smtp')dnl
Available mailers:
local— Local delivery (included automatically)smtp— SMTP variants (smtp, esmtp, smtp8, dsmtp, relay)uucp— UUCP delivery (legacy)procmail— Pipe to procmailcyrus— Cyrus mailbox formatfax— HylaFAX delivery
FEATURE Macros
Enable optional functionality:
FEATURE(`use_cw_file')dnl # Read /etc/mail/local-host-names
FEATURE(`use_ct_file')dnl # Read trusted users
FEATURE(`mailertable')dnl # Enable mailer table routing
FEATURE(`virtusertable')dnl # Virtual domain hosting
FEATURE(`always_add_domain')dnl # Add domain to local addresses
FEATURE(`generics_entire_domain')dnl # Apply generics to subdomains
Configuration for Laravel
Step 1: Create/Edit .mc File
Create /etc/mail/sendmail.mc:
VERSIONID(`$Id: sendmail.mc,v 1.0 2024/01/01 $')dnl
OSTYPE(`linux')dnl
DOMAIN(`generic')dnl
FEATURE(`use_cw_file')dnl
FEATURE(`mailertable')dnl
FEATURE(`always_add_domain')dnl
define(`confLOG_LEVEL', `9')dnl
define(`confDOMAIN_NAME', `example.com')dnl
LOCAL_CONFIG
# Local rules can go here
MAILER(`local')dnl
MAILER(`smtp')dnl
Step 2: Compile to .cf File
cd /etc/mail
m4 m4/cf.m4 sendmail.mc > sendmail.cf
sudo chown root:wheel sendmail.cf
sudo chmod 644 sendmail.cf
Step 3: Restart Sendmail
sudo systemctl restart sendmail
Step 4: Configure Laravel
Update .env:
MAIL_MAILER=sendmail
MAIL_SENDMAIL="/usr/sbin/sendmail -bs"
Update config/mail.php:
return [
'default' => env('MAIL_MAILER', 'sendmail'),
'mailers' => [
'sendmail' => [
'transport' => 'sendmail',
'path' => env('MAIL_SENDMAIL', '/usr/sbin/sendmail -bs'),
],
],
];
Clear Laravel's configuration cache:
php artisan config:cache
Address Rewriting and Masquerading
Masquerading (Hiding Internal Addresses)
Rewrite outgoing mail addresses to appear to come from your domain:
MASQUERADE_AS(`example.com')dnl
FEATURE(`masquerade_envelope')dnl # Masquerade envelope too
Example:
- Internal user:
john@mail.example.com - Appears as:
john@example.com
Masquerade Exceptions
Exclude certain hosts from masquerading:
MASQUERADE_EXCEPTION(`localhost')dnl
MASQUERADE_EXCEPTION(`mail.example.com')dnl
Generic Maps (Address Rewriting)
Rewrite local addresses to different domains:
Create /etc/mail/genericstable:
root admin@example.com
www-data web@example.com
@localhost user@example.com
Add to .mc file:
FEATURE(`generics_entire_domain')dnl
define(`GENERICS_TABLE_DOMAIN_NAMES', `example.com')dnl
Build the map:
makemap hash /etc/mail/genericstable < /etc/mail/genericstable
sudo systemctl restart sendmail
Virtual Domains
Virtual User Table
Host multiple domains without creating system users:
Create /etc/mail/virtusertable:
support@example.com localuser
admin@example.net admin@example.com
error@another.org /dev/null
info@ error "550 No such user here"
Add to .mc file:
FEATURE(`virtusertable')dnl
Build the map:
makemap hash /etc/mail/virtusertable < /etc/mail/virtusertable
sudo systemctl restart sendmail
Map entries explained:
address@domain username— Deliver to local useraddress@domain user@other.com— Forward to external addressaddress@domain /dev/null— Silently discard@domain error "message"— Reject with error message
Relaying and Routing
Smart Host (Central Relay)
Send all outgoing mail through a relay server:
define(`SMART_HOST', `smtp.isp.com')dnl
Mail Hub (Local Host Relay)
Send mail for local hostnames to a central hub:
define(`MAIL_HUB', `mailhub.example.com')dnl
Local Relay (Unqualified Names)
Forward unqualified addresses (just username) to a relay:
define(`LOCAL_RELAY', `relay.example.com')dnl
Mailer Tables
Route mail for specific domains to different mail servers:
Create /etc/mail/mailertable:
.example.com smtp:mail.example.com
partner.com smtp:[partner-mail.com]:2525
legacy.org uucp-new:legacyhost!
. smtp:[smtp.isp.com]
Add to .mc file:
FEATURE(`mailertable')dnl
Build the map:
makemap hash /etc/mail/mailertable < /etc/mail/mailertable
sudo systemctl restart sendmail
Syntax:
.domain— Matches domain and subdomainsdomain— Exact match only.— Default/catch-all
Security Configuration
TLS/STARTTLS
Enable encrypted SMTP connections:
define(`confCACERT_PATH', `/etc/ssl/certs')dnl
define(`confCACERT', `/etc/ssl/certs/ca-certificates.crt')dnl
define(`confSERVER_CERT', `/etc/letsencrypt/live/mail.example.com/fullchain.pem')dnl
define(`confSERVER_KEY', `/etc/letsencrypt/live/mail.example.com/privkey.pem')dnl
SMTP Authentication
Allow authenticated relay (combat spam):
define(`confAUTH_MECHANISMS', `EXTERNAL GSSAPI DIGEST-MD5 CRAM-MD5 LOGIN PLAIN')dnl
define(`confAUTH_OPTIONS', `A p')dnl
Anti-Spam Rules
Restrict relaying to authorized users:
FEATURE(`relay_hosts_only')dnl
FEATURE(`loose_relay_check')dnl
LOCAL_CONFIG
R$* $: $&{client_name}
Custom Rulesets
LOCAL_RULE_3 (Canonicalization)
Customize address canonicalization:
LOCAL_RULE_3
# Map old hosts to new domains
R$* @ oldhost $* $1 @ newhost.example.com $2
LOCAL_RULESETS (Custom Rules)
Add completely custom rulesets:
LOCAL_RULESETS
Scheck_from_ok
R$* $: ok
LOCAL_CONFIG (Custom Declarations)
Define custom maps and variables:
LOCAL_CONFIG
KscheduleMap hash -o /etc/mail/schedulemap
Kldap ldap -1 -v sendmailMTAmap -b dc=example,dc=com
Using Lookup Tables
Berkeley DB Maps
Default map type for most Sendmail configurations:
makemap hash /etc/mail/mapname < /etc/mail/mapname.txt
Change Default Map Type
define(`DATABASE_MAP_TYPE', `dbm')dnl
LDAP Integration
Query LDAP directory for lookups:
LOCAL_CONFIG
Kldap_aliases ldap -1 -v mail -b ou=aliases,dc=example,dc=com
Kldap_users ldap -1 -v mailLocalAddress -b ou=people,dc=example,dc=com
Troubleshooting Sendmail
Check Sendmail Status
sudo systemctl status sendmail
netstat -tulnp | grep sendmail
View Sendmail Logs
# Real-time monitoring
sudo tail -f /var/log/mail.log
# Search for errors
grep "ERROR" /var/log/mail.log
# Find rejected mail
grep "reject" /var/log/mail.log
# Trace a specific message
grep "MESSAGE_ID" /var/log/mail.log
Increase Log Level
Add to .mc file:
define(`confLOG_LEVEL', `9')dnl
Then recompile and restart.
Test Email Sending
Manually test Sendmail:
echo "Subject: Test Email
This is a test." | sendmail -v recipient@example.com
Verify Configuration
Check syntax before restarting:
sendmail -C /etc/mail/sendmail.cf -bi
Common Issues and Solutions
Issue: "sendmail not found"
sudo apt-get install sendmail sendmail-bin
Issue: "Emails not sending from Laravel"
- Check
proc_open()is not disabled inphp.ini - Verify
/usr/sbin/sendmailexists and is executable - Check web server user can execute sendmail
- Review logs:
tail -f /var/log/mail.log
Issue: "Permission denied on sendmail"
sudo chmod 4755 /usr/sbin/sendmail
sudo chown root:smmsp /usr/sbin/sendmail
Issue: ".cf file not found" errors
- Ensure you compiled
.mcto.cf:m4 /etc/mail/cf/m4/cf.m4 sendmail.mc > sendmail.cf - Restart Sendmail after moving
.cffile - Check file permissions:
sudo chmod 644 /etc/mail/sendmail.cf
Issue: "Relay access denied"
- Check SMART_HOST configuration
- Verify MASQUERADE_AS is set if needed
- Review access database:
makemap -q hash /etc/mail/access
Working with the Mail Queue
View Queue
# List all queued messages
mailq
# Count messages
mailq | wc -l
# Show just message IDs
mailq | grep "^[A-F0-9]"
Flush Queue (Force Delivery)
# Retry all messages
sendmail -q
# Force immediate processing
sendmail -q -v
# Process specific queue run
sendmail -qI <QUEUE_ID>
Remove Stuck Messages
# Remove specific message
sudo rm /var/spool/postfix/deferred/<QUEUE_ID>
# Clear entire queue (dangerous!)
sudo rm /var/spool/postfix/deferred/*
System Mail Forwarding
Redirect system mail (cron, services) to real person:
Create/Edit /etc/aliases:
root: admin@example.com, admin@backup-domain.com
www-data: web-admin@example.com
Build the alias database:
sudo newaliases
This ensures important system messages reach administrators, not root's inbox.
Performance Tuning
Connection Settings
define(`confTO_CONNECT', `10s')dnl
define(`confTO_INITIAL', `10s')dnl
define(`confTO_QUEUERETURN', `5d')dnl
define(`confMAX_HEADERS_LENGTH', `32768')dnl
Concurrency Limits
define(`confMAX_DAEMON_CHILDREN', `20')dnl
define(`confMAX_QUEUE_CHILDREN', `20')dnl
define(`confQUEUE_LA', `8')dnl
define(`confREFUSE_LA', `12')dnl
Process Limits
define(`confMAX_MESSAGE_SIZE', `100000000')dnl
define(`confNO_RCPT_ACTION', `add-to-undisclosed')dnl
Best Practices
Configuration Management
✅ Do:
- Keep
.mcfiles in version control - Backup
/etc/mail/sendmail.cfbefore changes - Test configuration in development first
- Document all customizations
- Use
m4to compile, never edit.cfdirectly
❌ Don't:
- Edit
.cffiles directly - Forget to rebuild when modifying
.mc - Use Sendmail on shared hosting (if
proc_openis disabled) - Keep passwords in
.mcfiles unencrypted
Security Checklist
- [ ] Run Sendmail as unprivileged user (sendmail)
- [ ] Restrict file permissions:
chmod 600for sensitive files - [ ] Enable TLS for SMTP connections
- [ ] Implement SASL authentication for relaying
- [ ] Configure SPF and DKIM records
- [ ] Monitor logs regularly
- [ ] Keep Sendmail updated
- [ ] Test open relay prevention
Monitoring
Regular checks to perform:
# Queue health
mailq | grep "^[A-F0-9]" | wc -l
# Recent errors
grep "ERROR" /var/log/mail.log | tail -20
# Authentication attempts
grep "AUTH" /var/log/mail.log | tail -10
# Rejected messages
grep "reject" /var/log/mail.log | tail -10
Advanced Topics
LDAP-Based Routing
Implement LDAP-based email routing with:
FEATURE(`ldap_routing')dnl
LOCAL_CONFIG
Kldaproute ldap -1 -v mailLocalAddress
Mail Filters (Milters)
Integrate SpamAssassin or other mail filters:
define(`confINPUT_MAIL_FILTERS', `spamcheck')dnl
define(`confMILTER_DEFAULT_ACTION', `accept')dnl
Queue Groups
Organize queue by destination:
QUEUE_GROUP(`fast', `P=/var/spool/mqueue-fast, F=')dnl
QUEUE_GROUP(`slow', `P=/var/spool/mqueue-slow, F=')dnl
Migration Guide
From Sendmail to Postfix
If moving away from Sendmail:
- Back up all Sendmail configs and maps
- Set up Postfix with equivalent configuration
- Test mail flow on development system
- Plan cutover during low-traffic window
- Monitor logs for issues
- Keep Sendmail running as backup initially
- Redirect SMTP port to Postfix when confident
Quick Reference
Common .mc Directives
VERSIONID(`...') # Version control
OSTYPE(`linux') # Operating system
DOMAIN(`generic') # Domain configuration
FEATURE(`use_cw_file') # Read /etc/mail/local-host-names
FEATURE(`mailertable') # Enable mailer routing
FEATURE(`virtusertable') # Virtual domains
MAILER(`local') # Local delivery
MAILER(`smtp') # SMTP delivery
define(`SMART_HOST', `...') # Relay server
define(`MASQUERADE_AS', `...') # Masquerade domain
Common Files
/etc/mail/sendmail.mc— Source configuration/etc/mail/sendmail.cf— Compiled configuration/etc/mail/local-host-names— Accepted hostnames/etc/mail/genericstable— Address rewriting/etc/mail/virtusertable— Virtual domains/etc/mail/mailertable— Routing rules/etc/aliases— Local aliases/var/log/mail.log— Sendmail logs/var/spool/mqueue/— Mail queue
Common Commands
m4 /etc/mail/cf/m4/cf.m4 sendmail.mc > sendmail.cf # Compile
makemap hash /etc/mail/mapname < /etc/mail/mapname.txt # Build maps
newaliases # Rebuild aliases
sendmail -C /etc/mail/sendmail.cf -bi # Verify
mailq # View queue
sendmail -q # Flush queue
sudo systemctl restart sendmail # Restart