💾

mail

21 notes  •  Databases

Fix Mail Permission Denied Errors on Linux

When Sendmail fails with a "Permission denied" error on /var/spool/mqueue-client/, the queue directory has incorrect ownership or permissions. This guide shows how to diagnose and fix the issue.

Prerequisites

  • Root or sudo access on the Linux server
  • Sendmail installed

Steps

Check the current permissions on the mail spool directory:

ls -l /var/spool/

The mqueue-client directory must be owned by root:smmsp. Fix the ownership:

chown -R root:smmsp /var/spool/mqueue-client/

If the permissions are still incorrect, set them to 770:

chmod 770 /var/spool/mqueue-client

Verify

Confirm the corrected ownership and permissions:

ls -l /var/spool/

Expected output for mqueue-client:

drwxrws--- 2 root smmsp 4096 ... mqueue-client

Then test sending mail:

sendmail -v user@example.com < /dev/null

Notes

  • The smmsp group is the Sendmail message submission program group. It must exist on the system.
  • If the group does not exist, create it: groupadd smmsp
  • The sticky bit (s) on the group permissions is normal for this directory.

Send Email from the Linux Command Line

The mail command lets you send email directly from the Linux command line or shell scripts. It relies on a local MTA such as Postfix being available on port 25. This guide covers installation and common usage patterns.

Prerequisites

  • A running local MTA (Postfix, Sendmail, or similar)
  • Root or sudo access for package installation

Steps

Install the mail command

apt-get install mailutils

Send a basic email

Run the command, type your message body, then press Ctrl-D on a new line to send:

mail -s "Hello World" someone@example.com

Send with body inline (one-liner)

echo "This is the body" | mail -s "Subject" someone@example.com

Send from a file

mail -s "Hello World" user@example.com < /path/to/message.txt

Specify CC and BCC

mail -s "Hello" user@example.com -c cc@example.com -b bcc@example.com

Send to multiple recipients

mail -s "Hello" user1@example.com,user2@example.com

Set a custom From address

echo "Message body" | mail -s "Subject" -aFrom:sender@example.com recipient@example.com

Send with verbose SMTP output

mail -v -s "Test" someone@example.com

Verify

Check the mail queue for any delivery failures:

mailq

Force immediate delivery of queued messages:

postqueue -f

Notes

  • The mail command from mailutils uses only a local SMTP server. To relay through an external SMTP server directly, use the heirloom-mailx package instead.
  • If you see "Cannot open mail:25", Postfix or your local MTA is not running. Start it with: systemctl start postfix
  • For sending attachments, use mutt: echo "Body" | mutt -a /path/to/file -s "Subject" -- recipient@example.com

Configure Custom Postfix Bounce Messages

Postfix 2.3 and later supports custom bounce message templates, letting you control the text sent back to senders when mail is undeliverable or delayed. This guide walks through checking your version and creating the template file.

Prerequisites

  • Postfix 2.3 or newer
  • Root access to the mail server

Steps

1. Verify Postfix version

postconf -d | grep mail_version

The output should show version 2.3 or higher.

2. Review queue lifetime settings

These values appear in the bounce templates. Check current and default values:

postconf -d | grep maximal_queue_lifetime
postconf -n | grep maximal_queue_lifetime
postconf -d | grep delay_warning_time
postconf -n | grep delay_warning_time

To change them:

postconf -e 'maximal_queue_lifetime = 1d'
postconf -e 'delay_warning_time = 4h'

Restart Postfix after changes:

systemctl restart postfix

3. Create the bounce template file

Create /etc/postfix/bounce.cf. The file must end with a blank line.

cat > /etc/postfix/bounce.cf << 'EOF'
failure_template = <<EOM
Charset: us-ascii
From: Mail Delivery Subsystem <MAILER-DAEMON@$myhostname>
Subject: Undelivered Mail Returned to Sender

Your message could not be delivered to the recipient. Please check the
address and try again, or contact your administrator.

The mail system
EOM

delay_template = <<EOM
Charset: us-ascii
From: Mail Delivery Subsystem <MAILER-DAEMON@$myhostname>
Subject: Mail Delivery Delayed

Your message has not been delivered yet after $delay_warning_time_hours hours.
The system will continue retrying for up to $maximal_queue_lifetime_days days.
EOM

success_template = <<EOM
Charset: us-ascii
From: Mail Delivery Subsystem <MAILER-DAEMON@$myhostname>
Subject: Mail Delivery Confirmation

Your message was successfully delivered.
EOM

EOF

4. Enable the template in main.cf

postconf -e 'bounce_template_file = /etc/postfix/bounce.cf'
systemctl reload postfix

Verify

Test the templates without sending real mail:

postconf -e 'soft_bounce = yes'
# Send a test to a non-existent address, then check logs
tail -f /var/log/mail.log

Restore soft_bounce after testing:

postconf -e 'soft_bounce = no'

Notes

  • Template variables like $myhostname, $delay_warning_time_hours, and $maximal_queue_lifetime_days are automatically expanded from main.cf.
  • You can use time-unit suffixes on variables: _seconds, _minutes, _hours, _days, _weeks.
  • Only include the templates you need; omitting a template uses the Postfix built-in default for that message type.

Set Up SMTP Mail Sending from AWS EC2 with Postfix

Sending email directly from an EC2 instance often results in messages being marked as spam or silently dropped due to missing reverse DNS records and IP reputation issues. The reliable solution is to configure Postfix as a local queuing relay that forwards all outbound mail through a trusted SMTP provider such as AuthSMTP.

Prerequisites

  • An AWS EC2 instance running Debian or Ubuntu
  • An account with an SMTP relay provider (e.g., AuthSMTP, SendGrid, SES)
  • Root or sudo access

Steps

1. Install Postfix

sudo apt-get install postfix

Select "Internet Site" when prompted.

2. Configure /etc/postfix/main.cf

myhostname = www.yourdomain.com
mydomain = yourdomain.com
myorigin = $mydomain
smtpd_banner = $myhostname ESMTP $mail_name
biff = no
append_dot_mydomain = no
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
mydestination = localdomain, localhost, localhost.localdomain, localhost
mynetworks = 127.0.0.0/8
mailbox_size_limit = 0
recipient_delimiter = +
inet_interfaces = all
relayhost = [mail.authsmtp.com]
smtp_connection_cache_destinations = mail.authsmtp.com
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = static:YOUR_SMTP_USER:YOUR_SMTP_PASSWORD
smtp_sasl_security_options = noanonymous
default_destination_concurrency_limit = 4
soft_bounce = yes

3. Restart Postfix

sudo systemctl restart postfix

Verify

Check the mail queue:

mailq

Force immediate delivery of queued messages:

sudo postqueue -f

Monitor the mail log:

sudo tail -f /var/log/mail.log

Notes

  • soft_bounce = yes causes Postfix to queue messages rejected by the relay gateway (e.g., authentication failures) instead of bouncing them back to the sender. Disable after initial testing.
  • default_destination_concurrency_limit = 4 keeps you within the SMTP provider's concurrent connection limits. Adjust if running Postfix on multiple instances.
  • Port 25 outbound is blocked by default on EC2. Use port 587 (submission) or request AWS to remove the restriction for port 25.
  • Avoid using Google Workspace as a relay; it has per-day sending limits.

Fix Common Postfix Errors

This guide covers three frequently encountered Postfix errors: LMTP socket connection failures, 550 rejection errors due to missing rDNS, and IPv6-related delivery failures to Gmail.

Prerequisites

  • Root access to the Postfix server
  • Access to /etc/postfix/main.cf

Steps

Error: "connect to ... /var/lib/imap/socket/lmtp: No such file or directory"

This error means Postfix is configured to deliver mail via Cyrus IMAP's LMTP socket, which is not present. Disable the setting in /etc/postfix/main.cf:

vi /etc/postfix/main.cf

Find and comment out the line:

#mailbox_transport = lmtp:unix:/var/imap/socket/lmtp

Reload Postfix:

systemctl reload postfix

Error: "550 action not taken"

This typically means the sending server's IP address has no matching reverse DNS (PTR) record. Set up rDNS for the server's IP address through your hosting provider or DNS registrar. The PTR record should resolve to your mail server hostname.

Error: "connect to gmail-smtp-in.l.google.com: Network is unreachable"

Postfix is attempting to deliver over IPv6 but IPv6 connectivity is not available or misconfigured. Force IPv4 delivery:

vi /etc/postfix/main.cf

Change:

inet_protocols = all

To:

inet_protocols = ipv4

Reload Postfix and flush the queue:

systemctl reload postfix
postfix flush

Verify

Check the mail log after each fix:

tail -f /var/log/mail.log

Send a test message:

echo "Test" | mail -s "Test" you@gmail.com

Troubleshooting

  • Use postqueue -p to view queued messages and their error details.
  • Use postsuper -d ALL to clear the queue after fixing configuration issues (use with caution).
  • Check rDNS setup using: dig -x YOUR_SERVER_IP

Fix Roundcube "Connection to Storage Server Failed"

After a fresh VestaCP installation on Ubuntu, Roundcube may display "Connection to storage server failed." This error occurs when Roundcube cannot connect to the IMAP server (Dovecot) due to permission or hostname resolution issues.

Prerequisites

  • VestaCP installed on Ubuntu with Roundcube
  • Root access to the server

Steps

1. Fix permissions on network configuration files

chmod 644 /etc/hosts /etc/resolv.conf /etc/host.conf

2. Ensure localhost is correctly defined in /etc/hosts

grep -q "127.0.0.1 localhost" /etc/hosts || echo "127.0.0.1 localhost localhost.localdomain" >> /etc/hosts

3. Verify IMAP is listening on port 143

telnet localhost 143

Expected output indicates a successful connection:

Connected to localhost.
* OK [CAPABILITY IMAP4rev1 ...] Dovecot ready.

Press Ctrl-] then type quit to exit telnet.

4. Restart the web server

systemctl restart apache2

Or for Nginx:

systemctl restart nginx

Verify

Open Roundcube in a browser and log in. The "Connection to storage server failed" error should no longer appear.

Troubleshooting

  • If telnet to port 143 fails, Dovecot is not running. Start it: systemctl start dovecot
  • Check Dovecot logs for errors: journalctl -u dovecot -n 50
  • Verify Roundcube configuration points to localhost as the IMAP host in /etc/roundcube/config.inc.php

Configure DNS Records for Proper Email Delivery (SPF, DKIM, MX)

Correct DNS configuration is essential for reliable email delivery. Missing or incorrect MX, SPF, or DKIM records cause mail to be rejected or marked as spam. This guide covers the required DNS records for a self-hosted mail server.

Prerequisites

  • A domain you control with access to its DNS settings
  • Your mail server IP address and hostname
  • DKIM key pair generated on your mail server (if configuring DKIM)

Steps

1. Set the MX record

The MX record tells other mail servers where to deliver email for your domain.

yourdomain.com.    IN  MX  10  mail.yourdomain.com.

Also add an A record pointing your mail hostname to your server IP:

mail.yourdomain.com.  IN  A  YOUR_SERVER_IP

2. Configure SPF

SPF authorizes which servers may send email for your domain:

yourdomain.com.  IN  TXT  "v=spf1 ip4:YOUR_SERVER_IP -all"

If you also send through Google or another provider, include their mechanism:

"v=spf1 ip4:YOUR_SERVER_IP include:_spf.google.com ~all"

3. Configure DKIM

Generate a DKIM key pair on your mail server (example using OpenDKIM):

opendkim-genkey -t -s mail -d yourdomain.com

Publish the public key as a DNS TXT record:

mail._domainkey.yourdomain.com.  IN  TXT  "v=DKIM1; k=rsa; p=YOUR_PUBLIC_KEY"

4. Configure DMARC

_dmarc.yourdomain.com.  IN  TXT  "v=DMARC1; p=quarantine; rua=mailto:dmarc@yourdomain.com"

5. Set a PTR (reverse DNS) record

Contact your server hosting provider to set a PTR record for your server IP that resolves to your mail hostname:

YOUR_SERVER_IP  PTR  mail.yourdomain.com.

Verify

Check MX records:

dig MX yourdomain.com

Check SPF record:

dig TXT yourdomain.com

Check DKIM record:

dig TXT mail._domainkey.yourdomain.com

Use an online tool such as MXToolbox (mxtoolbox.com) for a comprehensive check.

Notes

  • DNS changes can take up to 48 hours to propagate globally, though typically propagate within 1 hour.
  • Use ~all (softfail) in SPF while testing; switch to -all (hardfail) once confirmed.
  • DMARC requires both SPF and DKIM to be set up and passing before it takes effect.

Install and Configure SSL on Postfix/Dovecot

Enabling SSL/TLS on a Postfix and Dovecot mail server encrypts both incoming and outgoing connections, protecting credentials and message content in transit. This guide covers certificate preparation and configuration for both services on Ubuntu 16.04 with Postfix 3.1 and Dovecot 2.2.

Prerequisites

  • Ubuntu 16.04 or later with Postfix and Dovecot installed
  • A valid SSL certificate and private key for your mail server hostname
  • Root access to the server

Steps

1. Upload certificate files to the server

Copy your certificate, CA bundle, and private key to the server:

scp yourdomainname.crt yourdomainname.ca-bundle yourdomainname.key root@yourserver:/etc/ssl/

2. Concatenate certificate and CA chain

cat /etc/ssl/certs/yourdomainname.crt /etc/ssl/certs/yourdomainname.ca-bundle   >> /etc/ssl/certs/certificate.crt

3. Configure Postfix for SSL/TLS

Edit /etc/postfix/main.cf and add or update the following:

smtpd_tls_cert_file = /etc/ssl/certs/certificate.crt
smtpd_tls_key_file = /etc/ssl/private/yourdomainname.key
smtpd_use_tls = yes
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
smtpd_tls_security_level = may
smtp_tls_security_level = may

To enable the submission port (587) with STARTTLS, add to /etc/postfix/master.cf:

submission inet n - y - - smtpd
  -o syslog_name=postfix/submission
  -o smtpd_tls_security_level=encrypt
  -o smtpd_sasl_auth_enable=yes

Restart Postfix:

systemctl restart postfix

4. Configure Dovecot for SSL/TLS

Edit /etc/dovecot/conf.d/10-ssl.conf:

ssl = yes
ssl_cert = </etc/ssl/certs/certificate.crt
ssl_key = </etc/ssl/private/yourdomainname.key

Restart Dovecot:

systemctl restart dovecot

Verify

Test SMTP TLS on port 587:

openssl s_client -starttls smtp -connect mail.yourdomain.com:587

Test IMAPS on port 993:

openssl s_client -connect mail.yourdomain.com:993

Both commands should show a valid certificate chain in the output.

Notes

  • Standard mail ports: IMAP (143/993), POP3 (110/995), SMTP (25/465), Submission (587).
  • Ports 143, 110, 25, and 587 use STARTTLS (opportunistic TLS on a plain-text connection). Ports 993, 995, and 465 use implicit TLS.
  • Ensure your firewall allows the ports you enable. Open with: ufw allow 993/tcp

Install and Use Imapsync on CentOS/Fedora

Imapsync is a command-line IMAP migration tool that copies emails from one IMAP server to another. This guide covers installing Imapsync from the EPEL repository on CentOS or Fedora and performing a mailbox migration.

Prerequisites

  • CentOS 7/8 or Fedora with sudo access
  • IMAP access enabled and reachable on both source and destination mail servers
  • Credentials for both source and destination mailboxes

Steps

1. Enable the EPEL repository

sudo yum install epel-release

2. Install Imapsync

sudo yum install imapsync

3. Transfer emails between IMAP servers

imapsync   --host1 imap.source.example.com   --user1 user@example.com   --password1 SourcePassword   --ssl1   --host2 imap.dest.example.com   --user2 user@example.com   --password2 DestPassword   --ssl2

The sync will take time proportional to mailbox size. Review the output for errors when complete.

Verify

The final output shows transfer statistics. Confirm these fields show zero errors:

Total bytes error   : 0 (0.000 KiB)
Total messages error: 0

Log in to the destination mailbox via IMAP client or webmail and confirm emails are present.

Notes

  • To migrate from Gmail/G Suite, enable IMAP access in Gmail settings and use an App Password if 2-Step Verification is enabled.
  • Use --dry to perform a test run without actually copying messages.
  • Use --delete2 to remove messages from the destination that no longer exist on the source (use with caution).
  • For Ubuntu/Debian installation, see the Install Imapsync on Ubuntu/Debian guide.

Migrate from Google Workspace (G Suite) to Office 365

This guide describes how to migrate Gmail mailboxes from Google Workspace (formerly G Suite) to Microsoft Office 365 using an IMAP migration batch. The migration moves email only; calendar and contacts must be migrated separately.

Prerequisites

  • Global administrator access to both the Google Workspace and Office 365 tenants
  • An Office 365 subscription with Exchange Online licenses for all migrating users
  • Google Workspace users must create App Passwords (required because Office 365 is considered a "less secure app" by Google)
  • 2-Step Verification must be enabled for Google Workspace users before App Passwords can be created

Steps

1. Verify domain ownership in Office 365

In the Office 365 Admin Center, go to Setup > Domains and follow the wizard to add and verify your domain. Add the provided TXT record at your DNS registrar.

2. Create user mailboxes in Office 365

Add users under Users > Active users and assign Exchange Online licenses. Do not set the domain as the primary MX destination yet.

3. Create a CSV migration file

Create a file named migration.csv with the following format:

EmailAddress,UserName,Password
user1@yourdomain.com,user1@yourdomain.com,AppPassword1
user2@yourdomain.com,user2@yourdomain.com,AppPassword2

4. Create a migration endpoint in Office 365

In the Exchange Admin Center, go to Migration > Migration endpoints > New, select IMAP, and enter:

IMAP server: imap.gmail.com
Port: 993
Security: SSL

5. Create and start the migration batch

Go to Migration > Add > Migrate to Exchange Online > IMAP migration, upload the CSV file, select your endpoint, and start the migration batch.

6. Update MX records to point to Office 365

Once migration is complete, update your domain MX record to the Office 365 mail server:

yourdomain.com.  IN  MX  0  yourdomain-com.mail.protection.outlook.com.

Verify

Monitor migration progress in the Exchange Admin Center under Migration. After MX update, send a test email to a migrated address and confirm delivery in the Office 365 mailbox.

Notes

  • IMAP migration does not move calendar events or contacts. Users must export and import those separately using Google Takeout and the Outlook import wizard.
  • Allow 24-72 hours for DNS changes to propagate.
  • For large migrations, use the PowerShell-based approach for more control and automation.

Fix PHP Mail Routed Through Hostname Instead of Domain

When PHP's mail() function sends email through the server hostname rather than your domain, the cause is typically an incorrect or missing envelope sender (return-path). This affects servers using cPanel/WHM with dedicated IP addresses and Exim as the MTA.

Prerequisites

  • PHP application using the mail() function or an SMTP library
  • Access to the PHP application code
  • A valid email address on the target domain

Steps

Option 1: Set the envelope sender via the mail() fifth parameter

The fifth parameter of PHP's mail() sets the envelope sender (MAIL FROM), which controls routing through dedicated IPs:

mail(
    "to@example.com",
    "Subject",
    "Message body",
    "From: sender@yourdomain.com",
    "-f sender@yourdomain.com"
);

The address passed to -f must be a real, existing address on the domain associated with the dedicated IP.

Option 2: Set the envelope sender in an SMTP library

If using PHPMailer or a similar library, set the Sender property explicitly:

<?php
use PHPMailer\PHPMailer\PHPMailer;
$mail = new PHPMailer();
$mail->Sender = 'sender@yourdomain.com';
$mail->From = 'sender@yourdomain.com';
$mail->FromName = 'Your Name';
$mail->addAddress('to@example.com');
$mail->Subject = 'Test';
$mail->Body = 'Test message';
$mail->send();

Test script to verify routing

<?php
$to = 'test@example.com';
$subject = 'Mail routing test';
$message = 'Testing envelope sender routing.';
$headers = 'From: sender@yourdomain.com';
$extra = '-f sender@yourdomain.com';
$result = mail($to, $subject, $message, $headers, $extra);
echo $result ? 'Mail sent' : 'Mail failed';
?>

Verify

Examine the raw email headers of a received test message. The Return-Path header should show your domain address, not the server hostname:

Return-Path: <sender@yourdomain.com>

Notes

  • The PHP process must have permission to use the -f flag. On some servers, the safe_mode or sendmail_path setting in php.ini restricts this.
  • Check sendmail_path in php.ini and ensure it does not override the envelope sender.
  • On cPanel servers, the dedicated IP functionality for Exim must be enabled in WHM under Service Configuration > Exim Configuration Manager.

Install Imapsync on Ubuntu/Debian

Imapsync is not included in the default Ubuntu/Debian repositories and must be installed from source with its Perl dependencies. This guide covers building Imapsync from the official GitHub repository.

Prerequisites

  • Ubuntu 18.04/20.04/22.04 or Debian 10/11 with sudo access
  • Internet access to reach GitHub and CPAN

Steps

1. Install system dependencies

sudo apt-get install -y git gcc libssl-dev   libauthen-ntlm-perl libclass-load-perl libcrypt-ssleay-perl   libdata-uniqid-perl libdigest-hmac-perl libdist-checkconflicts-perl   libfile-copy-recursive-perl libio-compress-perl libio-socket-inet6-perl   libio-socket-ssl-perl libio-tee-perl libmail-imapclient-perl   libmodule-scandeps-perl libnet-ssleay-perl libpar-packer-perl   libreadonly-perl libregexp-common-perl libsys-meminfo-perl   libterm-readkey-perl libtest-fatal-perl libtest-mock-guard-perl   libtest-pod-perl libtest-requires-perl libtest-simple-perl   libunicode-string-perl liburi-perl libtest-nowarnings-perl   libtest-deep-perl libtest-warn-perl make cpanminus

2. Install additional Perl modules via CPAN

sudo cpanm Mail::IMAPClient JSON::WebToken Crypt::OpenSSL::RSA   Test::MockObject Unicode::String Data::Uniqid File::Tail   Encode::IMAPUTF7

3. Clone the Imapsync repository

git clone https://github.com/imapsync/imapsync.git
cd imapsync

4. (Optional) Migrate from Office 365 to Google Workspace using a batch script

Create a credentials file file.txt with CSV rows: source_user,source_pass,dest_user,dest_pass

#!/bin/bash
host1=outlook.office365.com
host2=imap.gmail.com
{ while IFS=',' read u1 p1 u2 p2; do
    echo "==== Starting imapsync $u1 -> $u2 ===="
    ./imapsync --no-modulesversion --nosslcheck       --host1 ${host1} --user1 "$u1" --password1 "$p1"       --host2 ${host2} --ssl2 --user2 "$u2" --password2 "$p2"       --nocheckfoldersexist --nocheckselectable
    echo "==== Done $u1 ===="
done } < file.txt

Verify

Run a self-test to confirm Imapsync is working:

./imapsync --tests

Exit code 0 means all tests passed.

Notes

  • For Gmail/G Suite as a source, enable IMAP in Gmail settings and use an App Password if 2-Step Verification is active.
  • Use --nosslcheck only for testing; remove it in production to validate TLS certificates.
  • Use --dry for a trial run that does not copy any messages.
  • Useful debug flags: --debug, --debugimap, --debugfolders

Set Up Email with Postfix and Dovecot

This guide walks through setting up a complete incoming and outgoing mail server using Postfix as the MTA and Dovecot for IMAP/POP3 access. At the end, a Linux system user will be able to send and receive email at username@yourdomain.com.

Prerequisites

  • A VPS or dedicated server with a public IP address
  • A domain name with DNS control
  • Root or sudo access
  • Postfix and Dovecot packages available in the distro repository

Steps

1. Set up DNS: MX record

Add an MX record at your DNS provider pointing to your server:

yourdomain.com.  IN  MX  10  mail.yourdomain.com.
mail.yourdomain.com.  IN  A  YOUR_SERVER_IP

2. Install Postfix and Dovecot

apt-get install postfix dovecot-imapd dovecot-pop3d

Select "Internet Site" when the Postfix installer prompts for a configuration type.

3. Create a mail user account

useradd --create-home -s /sbin/nologin emailusername
passwd emailusername

4. Configure Postfix destinations

Edit /etc/postfix/main.cf and set the domains Postfix should accept mail for:

mydestination = yourdomain.com, localhost.yourdomain.com, localhost
myhostname = mail.yourdomain.com
myorigin = yourdomain.com

Reload Postfix:

systemctl reload postfix

5. Enable SASL authentication (for newer distros)

apt-get install mail-stack-delivery

This configures Dovecot to provide SASL authentication to Postfix via a socket.

For older distributions (pre-Ubuntu 10.04):

chkconfig saslauthd on
/etc/init.d/saslauthd start

6. Verify Postfix is running

ps axf | grep postfix

You should see postfix/master, qmgr, and pickup processes.

Verify

Send a test email from the server command line:

echo "test" | mail -s "Test subject" someone@gmail.com

Check the mail log for delivery confirmation:

tail -f /var/log/mail.log

Notes

  • The MX record is required even if yourdomain.com already resolves to your server IP via an A record.
  • If receiving mail for multiple domains, list all of them in mydestination or use virtual_mailbox_domains.
  • Use MXToolbox or a similar DNS checker to confirm your MX record is propagated before testing.
  • Configure SPF, DKIM, and DMARC DNS records to improve deliverability and avoid spam classification.

Bulk IMAP Migration with Imapsync

When migrating multiple mailboxes simultaneously, a shell script wrapping Imapsync can process a list of accounts from a file. This guide shows how to build a bulk migration script and handle Gmail-specific requirements.

Prerequisites

  • Imapsync installed on a migration host (see Install Imapsync guide)
  • IMAP access enabled on both source and destination mail servers
  • A credentials file with one account per line
  • For Gmail/G Suite sources: IMAP enabled in account settings, App Passwords created if 2FA is active

Steps

1. Create the credentials file

Create accounts.txt with one account per line, space-separated: username and password.

user1@example.com password1
user2@example.com password2

2. Write the bulk migration script

#!/bin/bash
SOURCE_HOST=108.167.157.126
DEST_HOST=44.242.111.171

{ while IFS=' ' read u1 p1; do
    echo "==== Starting imapsync: $u1 ===="
    ./imapsync --no-modulesversion       --host1 "$SOURCE_HOST" --user1 "$u1" --password1 "$p1"       --host2 "$DEST_HOST"   --user2 "$u1" --password2 "$p1"
    echo "==== Finished: $u1 ===="
    echo
done } < accounts.txt

Save as bulk_sync.sh and make it executable:

chmod +x bulk_sync.sh

3. Migrate from Gmail/G Suite

Enable IMAP in each Gmail account under Settings > See all settings > Forwarding and POP/IMAP. If 2FA is enabled, generate an App Password for each account.

./imapsync --no-modulesversion   --host1 imap.gmail.com   --user1 user@example.com   --password1 'AppPassword'   --host2 imap.destinationhost.com   --user2 user@example.com   --password2 'DestPassword'

4. Run the migration

./bulk_sync.sh 2>&1 | tee migration.log

Verify

Review the log for errors:

grep -i "error\|failed\|abort" migration.log

Check per-account transfer summary lines at the end of each account block for zero error counts.

Notes

  • Store the credentials file with restricted permissions: chmod 600 accounts.txt
  • Use --justfoldersizes flag first for a quick check of folder sizes without migrating.
  • Run migrations in a screen or tmux session to prevent interruption if the SSH connection drops.
  • Use --nocheckfoldersexist --nocheckselectable flags when folders differ between source and destination.

Set Up Your Own Email Server on AWS

Running a self-hosted mail server on AWS gives full control over email infrastructure, storage, and security. This guide covers the key decisions and steps for deploying Postfix and Dovecot on an EC2 instance, including AWS-specific considerations such as port 25 restrictions and Elastic IPs.

Prerequisites

  • An AWS account with EC2 access
  • A domain name with DNS control
  • An EC2 instance running Ubuntu 20.04 or later (t3.small or larger recommended)
  • An Elastic IP address assigned to the instance
  • AWS support request approved to remove the port 25 restriction

Steps

1. Request removal of EC2 port 25 restriction

By default, AWS blocks outbound port 25 on EC2. Submit a request through the AWS Support Center to lift the restriction for your account and Elastic IP.

2. Assign and configure an Elastic IP

Allocate an Elastic IP in the EC2 console and associate it with your instance. This provides a stable IP for rDNS configuration.

3. Install Postfix and Dovecot

sudo apt-get update
sudo apt-get install postfix dovecot-imapd dovecot-pop3d dovecot-lmtpd

Select "Internet Site" when prompted. Set the mail name to your domain.

4. Configure Postfix main.cf

sudo postconf -e "myhostname = mail.yourdomain.com"
sudo postconf -e "mydomain = yourdomain.com"
sudo postconf -e "myorigin = \$mydomain"
sudo postconf -e "mydestination = \$myhostname, yourdomain.com, localhost"
sudo postconf -e "inet_interfaces = all"
sudo postconf -e "inet_protocols = ipv4"

5. Configure security groups

In the EC2 console, open the following inbound ports on the instance's security group:

25   (SMTP)
587  (Submission)
465  (SMTPS)
143  (IMAP)
993  (IMAPS)
110  (POP3)
995  (POP3S)

6. Set DNS records

# MX record
yourdomain.com.  IN  MX  10  mail.yourdomain.com.
# A record
mail.yourdomain.com.  IN  A  YOUR_ELASTIC_IP
# SPF
yourdomain.com.  IN  TXT  "v=spf1 ip4:YOUR_ELASTIC_IP -all"

Set a PTR (reverse DNS) record via the EC2 console under Elastic IPs > Actions > Update reverse DNS.

7. Restart services

sudo systemctl restart postfix dovecot

Verify

Test SMTP connectivity:

telnet mail.yourdomain.com 25

Send a test email and check logs:

echo "Test" | mail -s "Test" you@gmail.com
sudo tail -f /var/log/mail.log

Notes

  • SaaS email (Google Workspace, Microsoft 365) is often easier to maintain for small teams. Self-hosting is best when you need full data control or cost predictability at scale.
  • Monitor disk space regularly — mail spools can grow quickly. Set mailbox quotas in Dovecot.
  • Install SpamAssassin or use SES as a relay to improve spam filtering and deliverability.
  • Back up /var/mail and Postfix/Dovecot configuration files regularly.

Fix "Messages Weren't Sent" SMTP Error

The error "Unfortunately, messages from [x.x.x.x] weren't sent. Please contact your Internet service provider since part of their network is on our block list (S3140)" is issued by Microsoft when your server IP is on their Outlook/Hotmail blocklist. This guide explains how to identify and resolve the issue.

Prerequisites

  • Administrative access to your mail server
  • The public IP address of your mail server

Steps

1. Confirm the block

Send a test email from your domain to a Hotmail or Outlook address and examine the bounce message. The bounce should include the block code (S3140) and your server IP.

2. Check if your IP is on Microsoft's blocklist

Visit the Microsoft Smart Network Data Services (SNDS) portal:

https://sendersupport.olc.protection.outlook.com/snds/

3. Submit a delisting request

Use the Microsoft sender support form to request IP delisting:

https://sender.office.com/

Fill in your server IP address, contact email, and a description of your mail sending practices. Submit the form and wait for a response (typically within 24 hours).

4. Prevent future blocks

Clean up your mailing list by removing unverified or unresponsive email addresses. Ensure you have SPF and DKIM records configured:

# SPF record
yourdomain.com.  IN  TXT  "v=spf1 ip4:YOUR_SERVER_IP -all"

# DKIM (after generating keys with opendkim-genkey)
mail._domainkey.yourdomain.com.  IN  TXT  "v=DKIM1; k=rsa; p=YOUR_PUBLIC_KEY"

Enroll in the Microsoft JMRP (Junk Mail Reporting Program) to receive spam complaint notifications:

https://sendersupport.olc.protection.outlook.com/snds/JMRP.aspx

Verify

After receiving Microsoft's confirmation email, send another test message to a Hotmail/Outlook address and confirm delivery without bounce.

Notes

  • Newly acquired dedicated server IPs are sometimes already on blocklists due to previous tenant abuse.
  • Microsoft typically grants "conditional mitigation" — keep your list clean and complaint rates low to stay off the list.
  • Check your IP against multiple blocklists using MXToolbox Blacklist Check: https://mxtoolbox.com/blacklists.aspx
  • Maintain a complaint rate below 0.1% to avoid automatic future blocks.

Migrate from Zoho Mail to Office 365

This guide describes how to migrate email from Zoho Mail to Microsoft Office 365 using the built-in IMAP migration feature in the Microsoft 365 Admin Center.

Prerequisites

  • Global administrator access to Microsoft 365
  • Zoho Mail administrator access
  • IMAP access enabled in Zoho Mail for all migrating accounts
  • Office 365 mailboxes created for all migrating users

Steps

1. Enable IMAP in Zoho Mail

Log in to the Zoho Mail admin console and enable IMAP access:

https://mailadmin.zoho.com/cpanel/index.do#dashboard/general

Navigate to Mail Settings > IMAP Access and enable it for all users or on a per-user basis.

2. Disable 2FA if necessary

If accounts have two-factor authentication enabled, disable it temporarily during migration or generate App Passwords from the Zoho admin console.

3. Retrieve users in the Microsoft 365 Admin Center

Go to the migration dashboard in Microsoft 365:

https://admin.microsoft.com/Adminportal/Home#/EmailMigration/Status

Click Add migration batch and select IMAP migration. Enter the Zoho admin email when prompted and retrieve the user list.

4. Start the migration

Enter the Zoho email addresses and passwords for each user. The migration wizard will connect to Zoho's IMAP server (imap.zoho.com, port 993 with SSL) and copy all emails to the corresponding Office 365 mailboxes.

5. Update DNS MX records

After migration is complete, update your domain MX record to point to Office 365:

yourdomain.com.  IN  MX  0  yourdomain-com.mail.protection.outlook.com.

Verify

Monitor migration status at the migration dashboard URL above. After the MX record propagates, send a test email to a migrated address and confirm it arrives in Office 365.

Troubleshooting

  • If the migration shows "Incorrect remote server credentials", verify that IMAP is enabled in Zoho for that specific account and the password is correct.
  • If 2FA is blocking connections, generate and use an App Password in Zoho: My Account > Security > App-Specific Passwords
  • Zoho IMAP server: imap.zoho.com, port 993, SSL enabled

Troubleshoot SMTP Connectivity and Timeout Issues with Amazon SES

SMTP timeout errors when connecting to Amazon SES indicate that a TCP connection to the SES endpoint cannot be established. The most common causes are security group rules, network ACLs, or EC2 port 25 restrictions. This guide covers a systematic approach to diagnosing and resolving the issue.

Prerequisites

  • An AWS account with Amazon SES configured
  • An EC2 instance or other compute resource attempting to send email via SES
  • Access to AWS Console (VPC, Security Groups, EC2)

Steps

1. Test TCP connectivity to the SES SMTP endpoint

Run the following from the sending host, replacing the endpoint with your SES region's endpoint:

telnet email-smtp.us-east-1.amazonaws.com 587
telnet email-smtp.us-east-1.amazonaws.com 25
telnet email-smtp.us-east-1.amazonaws.com 465

Or use netcat:

nc -vz email-smtp.us-east-1.amazonaws.com 587

A successful connection returns:

220 email-smtp.amazonaws.com ESMTP SimpleEmailService ...

2. If connection times out — check firewall and ACLs

For EC2 instances, verify the following in the AWS Console:

  • Security group outbound rules: must allow TCP on port 25, 587, or 465 to the SES endpoint or to 0.0.0.0/0
  • Network ACL outbound rules: must allow TCP on the SMTP port to the SES endpoint
  • Network ACL inbound rules: must allow return traffic on ephemeral ports TCP 1024-65535
  • EC2 port 25 restriction: AWS blocks port 25 by default on EC2. Request removal via AWS Support.

3. Test SSL/TLS negotiation

If TCP connects but TLS fails, test SSL directly:

openssl s_client -crlf -connect email-smtp.us-east-1.amazonaws.com:465
openssl s_client -crlf -starttls smtp -connect email-smtp.us-east-1.amazonaws.com:587

Expected responses include SMTP 220 and SMTP 250. If TLS fails:

  • Verify the SSL certificate store is correctly configured
  • Confirm the sending application has the correct CA bundle path
  • Verify the Amazon SES certificate is trusted by your system

4. Use port 2587 as an alternative

If port 587 is blocked and port 25 is restricted, Amazon SES also supports port 2587 for STARTTLS connections.

Verify

After addressing connectivity issues, send a test message via the AWS CLI:

aws ses send-email   --from sender@yourdomain.com   --to recipient@example.com   --subject "SES Test"   --text "Test message body"   --region us-east-1

Check the SES console for send statistics and any delivery errors.

Notes

  • SES SMTP credentials are not the same as AWS access keys. Generate them in the SES console under SMTP Settings.
  • SES SMTP endpoints by region: email-smtp.us-east-1.amazonaws.com, email-smtp.eu-west-1.amazonaws.com, etc.
  • For Lambda or container workloads, verify that the VPC NAT gateway and route tables allow outbound internet access on the required port.

Integrate Amazon SES with Postfix

Configuring Postfix to relay outbound email through Amazon SES provides managed deliverability, bounce handling, and sending statistics. This guide covers installing prerequisites and configuring Postfix to authenticate with SES over STARTTLS on port 587.

Prerequisites

  • Linux server (Ubuntu, Debian, CentOS, or Amazon Linux) with Postfix installed
  • Sendmail removed if previously installed
  • SASL authentication library installed:
    • Ubuntu/Debian: apt-get install libsasl2-modules
    • CentOS/RHEL: yum install cyrus-sasl-plain
  • Amazon SES SMTP credentials (generated in the SES console under SMTP Settings)
  • A verified sender email address or domain in SES

Steps

1. Configure Postfix to relay through SES

sudo postconf -e "relayhost = [email-smtp.us-east-1.amazonaws.com]:587"   "smtp_sasl_auth_enable = yes"   "smtp_sasl_security_options = noanonymous"   "smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd"   "smtp_use_tls = yes"   "smtp_tls_security_level = encrypt"   "smtp_tls_note_starttls_offer = yes"

Replace email-smtp.us-east-1.amazonaws.com with the endpoint for your AWS region.

2. Disable the smtp_fallback_relay setting

Open /etc/postfix/master.cf and comment out this line if present:

#-o smtp_fallback_relay=

3. Create the SASL credentials file

sudo vi /etc/postfix/sasl_passwd

Add the following line (replace with your SES SMTP credentials):

[email-smtp.us-east-1.amazonaws.com]:587 SMTPUSERNAME:SMTPPASSWORD

4. Secure and hash the credentials file

sudo chown root:root /etc/postfix/sasl_passwd
sudo chmod 600 /etc/postfix/sasl_passwd
sudo postmap hash:/etc/postfix/sasl_passwd
sudo chown root:root /etc/postfix/sasl_passwd.db
sudo chmod 600 /etc/postfix/sasl_passwd.db

5. Restart Postfix

sudo systemctl restart postfix

Verify

Send a test email:

echo "Test body" | mail -s "SES Test" recipient@example.com

Check the mail log for successful relay:

sudo tail -f /var/log/mail.log

Look for a line containing status=sent and the SES endpoint hostname.

Notes

  • SES SMTP credentials are separate from AWS IAM credentials. Do not use your AWS access key ID and secret key as SMTP credentials.
  • If your account is in SES sandbox mode, you can only send to verified email addresses. Request production access in the SES console.
  • For high-volume sending, configure smtp_destination_concurrency_limit and smtp_destination_rate_delay to stay within SES rate limits.

Migrate Google Workspace to Office 365 with PowerShell

This guide uses the Exchange Online PowerShell module to create a Gmail migration endpoint and batch in Office 365, enabling a service-account-based migration from Google Workspace without requiring individual App Passwords.

Prerequisites

  • Windows with PowerShell 5.1 or later
  • Exchange Online PowerShell V2 module installed
  • Global Administrator credentials for the Office 365 tenant
  • A Google Cloud service account with Gmail API access and a downloaded JSON key file
  • A CSV file listing mailboxes to migrate

Steps

1. Install and import the Exchange Online module

Install-Module -Name ExchangeOnlineManagement -Force
Import-Module ExchangeOnlineManagement

2. Connect to Exchange Online

Connect-ExchangeOnline -UserPrincipalName admin@yourdomain.com

3. Test connectivity to Gmail using the service account

Test-MigrationServerAvailability `
  -Gmail `
  -ServiceAccountKeyFileData $([System.IO.File]::ReadAllBytes("C:\path	o\serviceaccount.json")) `
  -EmailAddress admin@yourdomain.com

4. Create the Gmail migration endpoint

New-MigrationEndpoint `
  -Gmail `
  -ServiceAccountKeyFileData $([System.IO.File]::ReadAllBytes("C:\path	o\serviceaccount.json")) `
  -EmailAddress admin@yourdomain.com `
  -Name gmailEndpoint

5. Create the migration batch

The CSV file must have an EmailAddress column listing all source Gmail addresses:

New-MigrationBatch `
  -SourceEndpoint gmailEndpoint `
  -Name gmailBatch `
  -CSVData $([System.IO.File]::ReadAllBytes("C:\path	o\migration.csv")) `
  -TargetDeliveryDomain "yourtenant.mail.onmicrosoft.com"

6. Start the migration batch

Start-MigrationBatch -Identity gmailBatch

7. Monitor migration status

Get-MigrationBatch -Identity gmailBatch | Select-Object Status, TotalCount, SyncedCount, FailedCount

Verify

Check that all users show a Synced status:

Get-MigrationUser | Where-Object {$_.BatchId -eq "gmailBatch"} | Select-Object Identity, Status, Error

Log in to the Office 365 admin center and confirm email is present in migrated mailboxes.

Notes

  • The Google service account must have domain-wide delegation enabled in the Google Admin Console with the Gmail API scope (https://mail.google.com/).
  • After migration is complete and verified, update your domain MX record to Office 365 and complete the migration batch.
  • Complete the batch to stop synchronization: Complete-MigrationBatch -Identity gmailBatch

Configure Postfix to Send Mail via SendGrid

Postfix can relay all outbound email through SendGrid's SMTP service using API key authentication. This is a reliable alternative to direct delivery and improves deliverability through SendGrid's managed IP reputation.

Prerequisites

  • Postfix installed on Linux (Ubuntu, Debian, CentOS, or similar)
  • A SendGrid account with an API key that has Mail Send permissions
  • SASL libraries installed:
    • Ubuntu/Debian: apt-get install libsasl2-modules
    • CentOS/RHEL: yum install cyrus-sasl-plain

Steps

1. Update /etc/postfix/main.cf

smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_sasl_security_options = noanonymous
smtp_sasl_tls_security_options = noanonymous
smtp_tls_security_level = encrypt
header_size_limit = 4096000
relayhost = [smtp.sendgrid.net]:587

2. Create the credentials file

sudo vi /etc/postfix/sasl_passwd

Add your SendGrid API key (use the literal string apikey as the username):

[smtp.sendgrid.net]:587 apikey:YOUR_SENDGRID_API_KEY

3. Secure the credentials file and update Postfix hashtables

sudo chmod 600 /etc/postfix/sasl_passwd
sudo postmap /etc/postfix/sasl_passwd

4. Restart Postfix

sudo systemctl restart postfix

Verify

Send a test email:

echo "Test body" | mail -s "SendGrid relay test" recipient@example.com

Check the mail log for a successful relay via SendGrid:

sudo tail -f /var/log/mail.log

Also check the SendGrid dashboard under Activity for the outbound message.

Troubleshooting

  • If you see "No mechanism available" errors, install the missing SASL library for your distro (see Prerequisites).
  • If port 587 is blocked on your network, try port 2525 instead: change 587 to 2525 in both main.cf and sasl_passwd, then re-run postmap and restart Postfix.
  • If tlsmgr errors appear, edit /etc/postfix/master.cf and uncomment the line: tlsmgr unix - - n 1000? 1 tlsmgr
  • Check the mail log for detailed errors: tail -f /var/log/maillog (CentOS) or /var/log/mail.log (Ubuntu/Debian)
  • Ensure your SendGrid API key has the "Mail Send" permission scope enabled.