Proxmox Mail Relay - Setup

I tried to create my own mail server
I actually got it going but damn it was a problematic, and difficult thing to do. In the end after doing more research I gave up and put left the docker compose file in the development folder - it does actually work.
So now what. Well, I need a mail server and when you host services whether for internal use of otherwise most of them still use email and although more of these are allowing the use of webhooks and other integrations, the fact of the matter is I am not interested in signing up to more services to use these integrations.
So, what to do. Well since two of these services require Mail gun, I simply utilized its services and set up a mail relay on the Proxmox hypervisor which is pretty handy as that is where all these services run.
Proxmox by default has postfix installed and with a little reconfiguration you can get it to act as a relay to your cloud-based mail system (not mix personal and development work - creates all sorts of problems).
So here is the script to adjust the postfix file to do the job on the Proxmox hypervisor.
Hypervisor Configuration
#!/bin/bash
# Updated 01-02-2025
# Refer to the following along with fail2ban
# https://superuser.com/questions/576751/example-of-fail2ban-configuration-to-ban-servers-spamming-my-postfix-server
# Variables
MAIN_CF="/etc/postfix/main.cf"
SASL_PASSWD="/etc/postfix/sasl_passwd"
MAILGUN_SMTP="smtp.mailgun.org"
MAILGUN_PORT="587"
MAILGUN_USER="[email address you setup for the hypervisor"
MAILGUN_PASS="[email address password it generated when you set it up"
TIMESTAMP=$(date +%Y%m%d%H%M%S)
EMAIL_ADDRESS="[testing email address your sending too]"
# Install necessary SASL modules
apt-get install -y libsasl2-modules
#Make the mail log
touch /var/log/mail.log
# Backup existing main.cf file with timestamp
cp $MAIN_CF "${MAIN_CF}-${TIMESTAMP}.bak"
# Update main.cf file
cat <<EOF > $MAIN_CF
# See /usr/share/postfix/main.cf.dist for a commented, more complete version
# compatibility level - refer to base file
compatibility_level=3.6
myhostname=pxe.braedach.com
smtpd_banner = \$myhostname ESMTP \$mail_name (Debian/GNU)
biff = no
# Listen on all interfaces or the pxe wont relay from LXC
inet_interfaces = all
# appending .domain is the MUA's job.
append_dot_mydomain = no
# Setup mail logging
maillog_file = /var/log/mail.log
# Uncomment the next line to generate "delayed mail" warnings
#delay_warning_time = 4h
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
mydestination = \$myhostname, localhost.\$mydomain, localhost
relayhost = [$MAILGUN_SMTP]:$MAILGUN_PORT
mynetworks = [modify these as required - I use CIDR notation]
recipient_delimiter = +
# Mailgun SMTP settings
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:$SASL_PASSWD
smtp_sasl_security_options = noanonymous
smtp_tls_security_level = encrypt
smtp_tls_note_starttls_offer = yes
# Enforce policies
smtpd_recipient_restrictions =
permit_mynetworks,
reject_unauth_destination,
reject_unknown_reverse_client_hostname,
reject_non_fqdn_sender,
reject_non_fqdn_recipient,
reject_unknown_sender_domain,
reject_unknown_recipient_domain,
permit_auth_destination,
permit
# Additional restrictions
smtpd_sender_restrictions =
reject_unknown_sender_domain,
reject_unauth_pipelining
EOF
# Create sasl_passwd file with Mailgun credentials
echo "[$MAILGUN_SMTP]:$MAILGUN_PORT $MAILGUN_USER:$MAILGUN_PASS" > $SASL_PASSWD
# Secure sasl_passwd file
chmod 600 $SASL_PASSWD
# Create hash database for sasl_passwd
postmap $SASL_PASSWD
# Restart Postfix service
systemctl restart postfix
# Enable Postfix service to start on boot
systemctl enable postfix
# Run postfix check and ensure it returns nothing
POSTFIX_CHECK=$(postfix check)
if [ -n "$POSTFIX_CHECK" ]; then
echo "Warning: Postfix check returned the following issues:"
echo "$POSTFIX_CHECK"
else
echo "Postfix configuration is correct."
fi
echo "Postfix has been configured to use Mailgun SMTP server."
# Test email to verify setup with hostname
echo "Sending test email for verification..."
echo "This is a test email from $(hostname -f)" | mail -s "Test Email from $(hostname -f)" ${EMAIL_ADDRESS}
An email should hit your designated in box shortly thereafter
Now for the LXC containers
#!/bin/bash
# Postfix setup script for LXC Debian 12 container
set -euo pipefail
MAIL_SERVER="[FQDN of mail server]"
EMAIL_ADDRESS="[testing email address]"
# Email address of the LXC host - change as required
EMAIL_ADDRESS_HOST="security@$(hostname -f)"
MAIN_CF="/etc/postfix/main.cf"
TIMESTAMP=$(date +%Y%m%d%H%M)
apt-get update
apt-get install -y postfix rsyslog mailutils sudo
cp "$MAIN_CF" "${MAIN_CF}-${TIMESTAMP}.bak"
echo "$(hostname -f)" > /etc/mailname
rm -f /etc/postfix/generic
cp /usr/share/postfix/main.cf.debian "$MAIN_CF"
touch /etc/aliases
sed -i '/^root:/d;/^docker:/d' /etc/aliases
debconf-set-selections <<< "postfix postfix/mailname string $(hostname -f)"
debconf-set-selections <<< "postfix postfix/main_mailer_type string 'Satellite system'"
debconf-set-selections <<< "postfix postfix/relayhost string ${MAIL_SERVER}"
postconf -e "relayhost = $MAIL_SERVER"
postconf -e "myhostname = $(hostname -f)"
postconf -e "smtp_generic_maps = hash:/etc/postfix/generic"
postconf -e "smtp_helo_name = $(hostname -f)"
postconf -e "myorigin = /etc/mailname"
postconf -e "mydestination = \$myhostname, localhost.\$mydomain, localhost"
postconf -e "maillog_file = /var/log/mail.log"
cat > /etc/postfix/generic <<EOF
root@$(hostname -f) ${EMAIL_ADDRESS_HOST}
EOF
postmap /etc/postfix/generic
echo "root: ${EMAIL_ADDRESS}" >> /etc/aliases
newaliases
touch /var/log/mail.log
chown root:adm /var/log/mail.log
chmod 640 /var/log/mail.log
chmod 644 /etc/postfix/generic
systemctl enable postfix rsyslog
systemctl restart rsyslog postfix
if ! postfix check; then
echo "Error: Postfix check failed."
exit 1
else
echo "Postfix configuration is correct."
fi
echo "Sending test email from ${EMAIL_ADDRESS_HOST} via ${MAIL_SERVER}..."
echo "This is a test email." \
| mail -s "Test Email from ${EMAIL_ADDRESS_HOST}" "${EMAIL_ADDRESS}"
Another email address should hit your inbox from the LXC
Job done - works a treat.
#enoughsaid