Who This Is For
Use multiple AWS features to forward emails when your ISP blocks SMTP from your homelab for a low price point.
If you’re like me, you do not have a business class internet plan from your ISP. Therefore, it is likely that your ISP blocks SMTP whether it’s inbound, outbound, or bidirectionally. To get around this, Amazon SES, Amazon S3, and AWS Lambda are utilized. If you do not have your own FQDN, you can choose to integrate Amazon Route53 as well. I will break down my journey from the beginning of setting up your email server to sending/receiving emails from your homelab.
Pre-Requisites
- A homelab
- Internet connection
- A FQDN (fully qualified domain name)
- Minimal understanding of Docker Compose
- Ability to adjust your firewall and port forward
- Amazon AWS account
- READ THE MAILCOW DOCUMENTATION BEFORE DOING ANYTHING
Setting Up Your First E-Mail Server
I use Mailcow as my current email server software. Mailcow comes with a webserver, webmailer, ActiveSync (MS), antivirus, antispam, indexing, document scanner (Oletools), SQL (MariaDB), Cache (Redis), MDA, MTA, various web services etc. https://mailcow.email/
The mailserver suite with the ‘moo’ – 🐮 + 🐋 = 💕
- Pick your linux flavor, I chose Ubuntu 22.04 at the time of setting up the server. Ubuntu, Debian, Alma, and Rocky work out of the box. For the sake of this tutorial, all commands and configurations are based on Ubuntu 22.04
- Set up your VM / LXC (for you Proxmox people)
- Recommended minimum 4GB RAM, 2 vCPU, 40GB storage (20GB for the software)
- apt update and upgrade your machine to it up to date
- Install Docker (https://docs.docker.com/install/) and Docker Compose v2 (https://docs.docker.com/compose/install/)
- Make sure no other services are using the following ports with: netstat -tulpn | grep -E -w ’25|80|110|143|443|465|587|993|995|4190′
- If any of these ports are in use, turn off the services using them.
- Adjust your firewall to port forward all ports listed on https://docs.mailcow.email/getstarted/prerequisite-system/#incoming-ports to the IP of your Mailcow VM / machine / LXC
Setting Up DNS Records
DNS can be daunting for the newbie, DNS for email is a bit harder. Note: If using Cloudflare, do NOT proxy your DNS records for email. Set up 4 records (replace example.com with your FQDN)
Name | Type | Value
- mail | A | 1.2.3.4 (your IP)
- autodiscover | CNAME | mail.example.com
- autoconfigure | CNAME | mail.example.com
- @ | MX 10 | mail.example.com
You will also need some TXT records for handling of rejected emails, who is authorized to send on your behalf, where should other email servers send DMARC reports
Name | Type | Value
- @ | IN TXT | “v=spf1 mx a ~all”
- dkim._domainkey | IN TXT | “v=DKIM1; k=rsa, t=s, s=email; p=…” (This record should be created in the Mailcow UI and copied over)
- _dmarc | IN TXT | “v=DMARC1; p=reject; rua=mailto:[email protected]“
The above DNS records should be enough to get your instance started. There are more advanced records posted on the Mailcow DNS Setup documents page.
Install Mailcow
Command line to clone Mailcow under /opt su umask 0022 # <- Verify it is 0022 cd /opt git clone https://github.com/mailcow/mailcow-dockerized cd mailcow-dockerized
Then initialize mailcow with the bash script. When prompted, use your FQDN (mail.example.com) as a hostname. ./generate_config.sh
Open and edit the config file if you want or need to. It will be in /opt/mailcow-dockerized/mailcow.conf nano mailcow.conf
Start Mailcow!
In the /opt/mailcow-dockerized folder enter the following command: docker compose pull && docker compose up -d
Once all containers are running, open a browser to your mailcow instance. The admin page is at https://mail.example.com/admin and users without the /admin Default admin credentials are admin:moohoo I highly recommend setting up 2FA for the admin account
Updating
To update mailcow, navigate to /opt/mailcow-dockerized and run the ./update.sh script
Reverse Proxy And SSL
I have a reverse proxy server in a different VM, so 80/443 are heading there first. All other email ports are directly forwarded to mailcow. Set up your proxy hosts for mail.example.com, autodiscover.example.com, & autoconfigure.example.com pointing HTTPS and port 443.
Obtain a Let’s Encrypt certificate for each. Once you’ve obtained the cert, copy and paste the parts to your /opt/mailcow-dockerized/assets/ssl/mail.example.com/ fullchain and key files
Setup Your First Mailbox
Log into the admin console and go to email > configuration

Add a domain

Fill out the fields as best as you can. Use your FQDN example.com and not the CNAME record of mail.example.com. Default settings are fine.
Next, click on Mailboxes tab and create your first email address. Then return to the Domain tab once complete.
Click the DNS button on your domain.

I’ve redacted most information from my records but you will know if your record is correct by the green check mark on the right. If there is a number 2, that means the record is optional

These records should be entered and/or changed to match the “Correct Data” field. Here, we can see my problem of my Pointer Record (PTR) is managed by my ISP and they will not adjust it unless I am on a business plan. Without this record, your emails will be bounced. This is where we move to AWS.
Amazon Simple Email Server (SES)
If you do not have a FQDN already, head over to Amazon Route 53 to purchase a domain name.

Amazon SES is a very low cost service provided by AWS. Navigate to Amazon SES page to begin.

Select your region and set up your identities. You will be in their Sandbox environment and only able to send emails to your own, verified email addresses set up in Identities.
- Set up your domain Identity, use your mail.example.com. Check “Use a custom MAIL FROM domain” and enter your CNAME. If you are using Amazon Route 53, keep “Publish DNS records to Route53” enabled.
- Select Easy DKIM with RSA_2048_BIT DKIM bit length. If you are not using Amazon Route 53, uncheck Publish to Route 53. If you are using Amazon Route 53, leave this checked. Records will be automatically published.
- Next, you will need to verify ownership of the domain. You will need to create three CNAME records with your DNS provider. If using Amazon Route 53, this is already taken care of.
- Once you have set up your DNS records, your dashboard will change and show the domain as verified. IMPORTANT NOTE: YOU MUST DELETE YOUR MX RECORDS WHEN YOU CREATED YOUR MAILCOW DNS RECORDS. AMAZON SES ONLY ALLOWS FOR ONE MX RECORD. If AWS sees more than one MX record that is not what they said to use, they will delete your SES instance after 72 hours!
- Verify another identity using another email address, such as a gmail account. You will then need to verify the email.
Back To Mailcow To Set Up Amazon SES As A Forwarding Host
- From the Amazon SES dashboard, click SMTP Settings on the left menu.
- From here, you need to set up credentials to give to Mailcow to use Amazon SES as a forwarding relay. Click Create SMTP Credentials to begin. Leave everything as default and click Create User. Next, download the .csv file or copy the data.
- Go to your Mailcow admin page.
- Under System > Configuration click the Routing tab
- Enter your SMTP credentials created (Not the IAM username). Enter the SMTP endpoint with the port number 587 (use 2587 if mail ports are blocked by your ISP) and click Add.
- Navigate to your Email Configuration and edit the mailbox created. Under Sender Dependant Transport, select the new relay you just created.
Now you can send emails from your homelab e-mail server! However, because we have to remove our MX record pointing to our inbox, now we can’t receive them.
If We Can Send E-Mails But Not Receive Them, How Do We Fix This?
Solution: Use Amazon SES with Amazon S3 and AWS Lambda to send emails to a singular, external email address!
The following actions occur in this solution:
- A new email is sent from an external sender to your domain. Amazon SES handles the incoming email for your domain.
- An Amazon SES receipt rule saves the incoming message in an S3 bucket.
- An Amazon SES receipt rule triggers the execution of a Lambda function.
- The Lambda function retrieves the message content from S3, and then creates a new message and sends it to Amazon SES.
- Amazon SES sends the message to the destination server.
Following this write-up by AWS employee, , you should have an S3 bucket configured for saving messages, Lambda configured to retrieve new, inbound messages and forward to SES for delivery to your self-hosted Mailcow server.
https://aws.amazon.com/blogs/messaging-and-targeting/forward-incoming-email-to-an-external-destination/
Can You Get AWS To Let You Leave Their Sandbox Environment?
Probably not. But this shows how different AWS (or other cloud providers) services can work together to accomplish your goals or meet the needs of your business. I was unable to leave their sandbox environment with the response “We made this decision because we believe that your use case would impact the deliverability of our service and would affect your reputation as a sender. We also want to ensure that other Amazon SES users can continue to use the service without experiencing service interruptions.” Despite having all records properly set up and a 10/10 score for my mail domain, AWS appears to want businesses only. For someone with a small homelab who isn’t going to be sending mass marketing emails, they won’t make much money off the resources I’d be using. Despite the disappointment, I did learn a great deal about using a relay server for email as well as the AWS Lambda function and S3 buckets.
Final Fix To Send Emails From My Server And Domain
I was still adamant about sending emails from my own domain name. I refused to stop because one cloud provider shut me down. Turns out, there are plenty of other email services out there and they provide FREE tiered plans!
Mailgun, SMTP2GO, Brevo to name a few. Each with slightly different ‘free’ tiers and slightly different setups. I chose SMTP2GO because they do not require any special DNS records and instead add a header to indicate the mail initiated from your server. Following the same steps as above to add Sender Dependent Transport, add them to your Mailcow configuration and that’s it!
With SMTP2GO as a relay host, I can send up to 200 e-mails a day and up to 1,000 e-mails a month for FREE!
Why Did I Want An E-Mail Server?
This is a multi-parted answer. One reason was to just learn how to set up and manage an e-mail domain, despite the discouragement from r/homelab. I found it an enjoyable learning experience. I wanted to add e-mail sending to my homelab services that I run to give me notification of updates, warnings, and alerts. Another reason was so I could add my own e-mail from my own domain to my resume. I thought this might be a way to stand out in the crowd but my main reason was because I am tired of spam emails and wanted to find out who is selling my data!
With Mailcow, you are able to set up as many aliases as you want, easily. Any time I order food online, I use that restaurant name as the alias. Any time I sign up for a rewards program at a store, I use their name as the alias. I have dozens of e-mail aliases now to manage where my spam is coming from and can easily delete the alias to stop the spam.
Despite the people who tried telling me NOT to pursue this, it was well worth the effort. I learned a lot more about DNS as well as the safety and importance of maintaining an e-mail server. If you are not afraid of learning something new and challenging, I recommend giving this a shot.


