Postfix Mail Relay¶
hub2 runs Postfix as the central mail relay for all CharlieHub services. Emails are relayed through SendGrid for improved deliverability.
Overview¶
| Property | Value |
|---|---|
| Host | hub2 (51.68.235.106) |
| Service | Postfix 3.8.x |
| Relay | SendGrid SMTP |
| Domain | microshare.eu |
| PTR Record | mail.microshare.eu |
Configuration Date
SendGrid relay configured on 2026-01-07, replacing direct SMTP sending which had deliverability issues with Microsoft 365.
Architecture¶
┌─────────────────┐ ┌─────────────┐ ┌──────────────┐
│ CharlieHub │ │ hub1 │ │ SendGrid │
│ Services │────▶│ Postfix │────▶│ SMTP │────▶ Recipients
│ (CT1935, etc) │ │ + OpenDKIM │ │ Relay │
└─────────────────┘ └─────────────┘ └──────────────┘
Email Authentication¶
All authentication mechanisms are configured and verified:
SPF Record¶
microshare.eu TXT "v=spf1 include:sendgrid.net include:_spf.google.com ip4:151.80.58.99 ~all"
DKIM (via SendGrid)¶
SendGrid signs outbound emails with DKIM. DNS records:
| Record | Type | Value |
|---|---|---|
s1._domainkey.microshare.eu |
CNAME | s1.domainkey.u58662085.wl086.sendgrid.net |
s2._domainkey.microshare.eu |
CNAME | s2.domainkey.u58662085.wl086.sendgrid.net |
em6444.microshare.eu |
CNAME | u58662085.wl086.sendgrid.net |
DMARC Record¶
_dmarc.microshare.eu TXT "v=DMARC1; p=quarantine; rua=mailto:cpaumelle@microshare.io; ruf=mailto:cpaumelle@microshare.io; fo=1"
Policy Active
DMARC policy set to quarantine - emails failing authentication will be sent to spam.
PTR (Reverse DNS)¶
| IP Version | Address | PTR Record |
|---|---|---|
| IPv4 | 151.80.58.99 | mail.microshare.eu |
| IPv6 | 2001:41d0:305:2100::3f7e | mail.microshare.eu |
Both configured via OVH API (see Credentials section).
Forward DNS (mail.microshare.eu)¶
| Type | Value |
|---|---|
| A | 151.80.58.99 |
| AAAA | 2001:41d0:305:2100::3f7e |
SendGrid Configuration¶
Domain Authentication¶
- SendGrid domain ID:
29103814 - Domain:
microshare.eu - Status: Verified ✓
Postfix Relay Configuration¶
Key settings in /etc/postfix/main.cf:
relayhost = [smtp.sendgrid.net]:587
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_sasl_security_options = noanonymous
smtp_tls_security_level = encrypt
header_size_limit = 4096000
SASL Credentials¶
File /etc/postfix/sasl_passwd:
[smtp.sendgrid.net]:587 apikey:$SENDGRID_API_KEY
Credential Location
The SendGrid API key is stored in /root/.env on hub1. See Credentials section below.
Credentials¶
All email-related credentials are stored in /root/.env on hub2:
# SendGrid API (for SMTP relay)
SENDGRID_API_KEY=SG.xxxxx...
# OVH API for DNS management (microshare.eu)
# Used by Domain Manager for DNS records
OVH_APP_KEY=ef8d1c1f7bdc8f06
OVH_APP_SECRET=REDACTED_OVH_SECRET
OVH_CONSUMER_KEY=3c2a1fab3ccf695990fddb14632f2dbb
# OVH API for IP/PTR management (151.80.58.99)
# Separate credentials with IP management permissions
OVH_IP_APP_KEY=92fe9be8de50865b
OVH_IP_APP_SECRET=fa1815ee5c8b1feb4aa4b80a1497c275
OVH_IP_CONSUMER_KEY=47261cda3c536478b6acbf27e8811f72
Two OVH API Credentials
There are two sets of OVH API credentials:
- DNS credentials (OVH_APP_*): Used by Domain Manager for DNS record management
- IP credentials (OVH_IP_*): Used for PTR/reverse DNS management on the dedicated server IP
Sending Email¶
From hub1 directly¶
# Simple test
echo "Test message" | mail -s "Test Subject" -r "notifications@microshare.eu" recipient@example.com
# With proper From header
echo "Test message" | mail -s "Test Subject" \
-r "notifications@microshare.eu" \
-a "From: Service Name <notifications@microshare.eu>" \
recipient@example.com
From other services¶
Services should connect to hub2 postfix on port 25 via WireGuard:
import smtplib
from email.mime.text import MIMEText
msg = MIMEText("Hello from CharlieHub")
msg["Subject"] = "Notification"
msg["From"] = "notifications@microshare.eu"
msg["To"] = "recipient@example.com"
# Use hub2's WireGuard IP for your site
with smtplib.SMTP("REDACTED_IP", 25) as server: # hub2 WireGuard IP (UK)
server.send_message(msg)
Envelope Sender¶
Always set envelope sender
When sending programmatically, always set the envelope sender (MAIL FROM) to match the From header domain. Mismatches can cause delivery issues or silent drops.
Monitoring¶
Check mail queue¶
mailq
View mail logs¶
tail -f /var/log/mail.log
Check SendGrid stats¶
source /root/.env
curl -s "https://api.sendgrid.com/v3/stats?start_date=$(date +%Y-%m-%d)" \
-H "Authorization: Bearer $SENDGRID_API_KEY" | jq .
Check SendGrid suppression lists¶
source /root/.env
# Bounces
curl -s "https://api.sendgrid.com/v3/suppression/bounces" \
-H "Authorization: Bearer $SENDGRID_API_KEY" | jq .
# Blocks
curl -s "https://api.sendgrid.com/v3/suppression/blocks" \
-H "Authorization: Bearer $SENDGRID_API_KEY" | jq .
# Spam reports
curl -s "https://api.sendgrid.com/v3/suppression/spam_reports" \
-H "Authorization: Bearer $SENDGRID_API_KEY" | jq .
Troubleshooting¶
Email not delivered¶
-
Check mail log:
grep "message-id" /var/log/mail.log | tail -20 -
Check SendGrid accepted it: Look for
status=sentandrelay=smtp.sendgrid.netin logs -
Check SendGrid suppression lists: Recipient may be bounced/blocked
-
Check recipient spam folder: Even with SendGrid, new senders may land in spam initially
SendGrid authentication error¶
550 The from address does not match a verified Sender Identity
Solution: Ensure domain authentication is complete in SendGrid:
source /root/.env
curl -s "https://api.sendgrid.com/v3/whitelabel/domains" \
-H "Authorization: Bearer $SENDGRID_API_KEY" | jq ".[] | {id, domain, valid}"
Microsoft 365 marking as spam¶
M365 may still mark emails as spam (SCL:5) even with proper authentication. Solutions:
- Recipient adds Safe Sender: In Outlook settings, add microshare.eu
- M365 Admin creates transport rule: Bypass spam filtering for microshare.eu
- Time: Sender reputation builds over weeks of consistent legitimate sending
Postfix not relaying¶
# Check postfix status
systemctl status postfix
# Test SMTP connection to SendGrid
openssl s_client -connect smtp.sendgrid.net:587 -starttls smtp
# Verify SASL credentials
postmap -q "[smtp.sendgrid.net]:587" /etc/postfix/sasl_passwd
Services Using Email¶
| Service | Purpose | From Address |
|---|---|---|
| CT1935 (pescle-rodent) | Video notifications | notifications@microshare.eu |
| Domain Manager | Alert notifications | notifications@microshare.eu |
DNS Management¶
Add/Update DNS records via OVH API¶
import ovh
client = ovh.Client(
endpoint="ovh-eu",
application_key="ef8d1c1f7bdc8f06",
application_secret="REDACTED_OVH_SECRET",
consumer_key="3c2a1fab3ccf695990fddb14632f2dbb"
)
# Add TXT record
client.post("/domain/zone/microshare.eu/record",
fieldType="TXT",
subDomain="_dmarc",
target='"v=DMARC1; p=quarantine"',
ttl=3600
)
# Refresh zone
client.post("/domain/zone/microshare.eu/refresh")
Update PTR record via OVH API¶
import ovh
client = ovh.Client(
endpoint="ovh-eu",
application_key="92fe9be8de50865b",
application_secret="fa1815ee5c8b1feb4aa4b80a1497c275",
consumer_key="47261cda3c536478b6acbf27e8811f72"
)
# Update IPv4 reverse DNS
client.put("/ip/151.80.58.99/reverse/151.80.58.99",
reverse="mail.microshare.eu"
)
# Update IPv6 reverse DNS (delete and recreate)
ipv6 = "2001:41d0:305:2100::3f7e"
client.delete(f"/ip/{ipv6}/reverse/{ipv6}")
client.post(f"/ip/{ipv6}/reverse",
ipReverse=ipv6,
reverse="mail.microshare.eu"
)
History¶
| Date | Change |
|---|---|
| 2026-01-08 | Upgraded DMARC policy from p=none to p=quarantine |
| 2026-01-07 | Configured SendGrid relay, domain authentication |
| 2026-01-07 | Added DKIM via SendGrid (s1, s2 selectors) |
| 2026-01-07 | Added DMARC policy (p=none with reporting) |
| 2026-01-07 | Configured IPv4 PTR record (mail.microshare.eu) |
| 2026-01-07 | Configured IPv6 PTR record (mail.microshare.eu) |
| 2026-01-07 | Added AAAA record for mail.microshare.eu |
| 2026-01-07 | Stored all credentials in /root/.env on hub1 |
Related Documentation¶
- hub2 Overview - Main hub2 documentation
- WireGuard VPN - VPN connectivity to homelabs
- Network Layout - Network architecture