Debian 13 VM with Proxmox Mail Gateway 9 on Azure User Guide
Overview
This image runs Proxmox Mail Gateway 9 on Debian 13 (trixie). Proxmox Mail Gateway is an open source email security gateway: it sits between the internet and your own mail servers as a full mail proxy, and every message is checked for spam, viruses and policy violations before it is relayed on. It combines SpamAssassin scoring, Bayesian filtering, DNS blocklists, SPF and DKIM checks and greylisting with the ClamAV antivirus engine, and adds an object based rule system, a per user spam quarantine with daily reports, a tracking center that follows each message, and a web interface and REST API for all of it.
The software is installed unmodified from Proxmox's own package repository using the proxmox-mailgateway-container package, which is the complete Proxmox Mail Gateway without a Proxmox kernel, so the VM keeps the Debian cloud kernel built for Azure.
Secure by default. No login is baked into the image. On the first boot of every VM:
- a web administrator,
admin@pmg, is created with a random 24 character password written to a file onlyrootcan read - the web interface certificate, the API ticket signing key, the CSRF key, the SMTP TLS certificate and the local database TLS key are generated for that VM alone, so no two deployments share a secret
- current antivirus signatures are downloaded from the ClamAV project (no signature database ships in the image)
The gateway also starts closed: it relays mail for no domains until you add yours, and the internal relay port only accepts mail from the VM's own subnet.
What is included:
- Proxmox Mail Gateway 9.1 (
pmg-api,pmg-gui, rule system, quarantine, tracking center, REST API) - Postfix, SpamAssassin 4, ClamAV 1.4 and PostgreSQL 17, as packaged by Proxmox and Debian
- Opportunistic STARTTLS for SMTP with a per-VM certificate
- The free
pmg-no-subscriptionupdate repository configured; the subscription only repository present but disabled - A boot time helper that keeps the gateway's host name and mail domain resolvable on Azure
- 24/7 cloudimg support
Proxmox is a registered trademark of Proxmox Server Solutions GmbH in the EU, the U.S. and other countries. This image is built and supported by cloudimg and is not affiliated with, sponsored by or endorsed by Proxmox Server Solutions GmbH. Proxmox Mail Gateway is free software licensed under the GNU Affero General Public License v3.
Prerequisites
- An Azure subscription with permission to create virtual machines, network security groups and public IP addresses
- A mail domain you control, and access to its DNS so you can publish MX and SPF records
- The host name or IP address of the mail server that should receive filtered mail (your own server, reachable from the VM)
- An SSH key pair
VM size. Proxmox Mail Gateway keeps the ClamAV signature set in memory (about 1 GiB, briefly doubling while a signature update is loaded) and runs several SpamAssassin filter processes. We measured a Standard_B2s (4 GiB) test VM under a burst of 40 concurrent messages during a signature reload: memory in use peaked at 3.8 of 3.8 GiB with only 22 MiB available. We therefore recommend Standard_B2ms (2 vCPU, 8 GiB) or larger for production, and Standard_B2s for evaluation only.
Azure and outbound port 25: read this first
Receiving mail works on every subscription: inbound TCP port 25 is not restricted. Sending mail directly to other domains' mail servers on TCP port 25 depends on your subscription type. Microsoft's current policy (Troubleshoot outbound SMTP connectivity problems in Azure) is:
- Enterprise Agreement and Microsoft Customer Agreement for enterprise (MCA-E): outbound port 25 is not blocked. Microsoft notes there is no guarantee that other domains will accept mail from Azure IP addresses.
- Enterprise Dev/Test: blocked by default. You can have the block removed by running the Cannot send email (SMTP-Port 25) diagnostic under Diagnose and solve problems on your virtual network in the Azure portal; then stop, deallocate and start the VM.
- All other subscription types (for example Pay-As-You-Go, Visual Studio, CSP, free trial): the Azure platform blocks outbound port 25, and Microsoft recommends an authenticated SMTP relay instead.
What this means for a mail gateway:
- Inbound filtering to a mail server you reach privately (in the same virtual network, a peered network, or over VPN or ExpressRoute) works on every subscription, because the block only applies to traffic routed directly to the internet.
- Relaying to a mail server over the internet on port 25, or sending outbound mail straight to recipients' MX hosts, only works on subscriptions where port 25 is open. Otherwise, send outbound mail through a smart host that accepts mail on port 587, such as Azure Communication Services or another SMTP relay (see Step 10). Connections to relays on port 587 are not restricted on any subscription type.
You can check your own subscription from the VM in Step 7.
Step 1: Deploy from the Azure Marketplace
- Open the Debian 13 VM with Proxmox Mail Gateway 9 listing by cloudimg in the Azure Marketplace and select Create.
- Choose a resource group, region and a VM size (see Prerequisites).
- Set the administrator username (for example
azureuser) and paste your SSH public key. - On Networking, allow inbound 22, 25, 26 and 8006 (Step 3 explains what each is for and how to restrict them).
- Select Review + create, then Create.
Step 2: Deploy with the Azure CLI (alternative)
az vm create \
--resource-group my-mail-rg \
--name pmg01 \
--image <marketplace-image-urn> \
--size Standard_B2ms \
--admin-username azureuser \
--ssh-key-values ~/.ssh/id_rsa.pub \
--public-ip-sku Standard
Step 3: Network security group rules
The gateway listens on exactly four ports off the VM:
| Port | Purpose | Recommended source |
|---|---|---|
| 22/TCP | SSH administration | your admin IP range only |
| 25/TCP | SMTP external port: mail from the internet to your domains | Internet |
| 26/TCP | SMTP internal port: outgoing mail from your own mail servers | your internal mail servers only |
| 8006/TCP | HTTPS web interface and API | your admin IP range only |
Everything else (PostgreSQL, the filter daemons, the policy service, the API daemon) listens on the loopback interface only. Restrict the administrative ports, for example:
az network nsg rule create --resource-group my-mail-rg --nsg-name pmg01NSG \
--name allow-pmg-admin --priority 1010 --direction Inbound --access Allow --protocol Tcp \
--source-address-prefixes <your-mgmt-cidr> --destination-port-ranges 22 8006
Step 4: Connect over SSH
ssh azureuser@<public-ip>
Step 5: Confirm the gateway is running
systemctl is-active pmgproxy pmgdaemon pmg-smtp-filter pmgpolicy postfix postgresql@17-main clamav-daemon clamav-freshclam
pmgversion
sudo ss -tln | grep -E ':(22|25|26|8006)\s'
Every service reports active, pmgversion shows the running Proxmox Mail Gateway API version and the Debian cloud kernel, and the listeners are SSH, the two SMTP ports and the web interface:

On the very first boot, allow about a minute for the antivirus signatures to download before clamav-daemon reports active.
Step 6: Retrieve your administrator login
sudo ls -l /root/proxmox-mail-gateway-credentials.txt
sudo cat /root/proxmox-mail-gateway-credentials.txt
The file is readable only by root and contains the per-VM administrator login and the web address:
PMG_ADMIN_USER=admin@pmg
PMG_ADMIN_PASSWORD=<generated on first boot>
PMG_URL=https://<public-ip>:8006/
PMG_PRIVATE_URL=https://<private-ip>:8006/
PMG_FQDN=<vm-name>.<azure-dns-suffix>
You can prove the login works from the shell without typing the password. The password is read from the file and passed to curl on standard input:
PMG_USER=$(sudo awk -F= '$1=="PMG_ADMIN_USER"{print $2}' /root/proxmox-mail-gateway-credentials.txt)
sudo awk -F= '$1=="PMG_ADMIN_PASSWORD"{printf "%s", $2}' /root/proxmox-mail-gateway-credentials.txt \
| curl -sk -d "username=${PMG_USER}" --data-urlencode 'password@-' https://127.0.0.1:8006/api2/json/access/ticket \
| python3 -c 'import sys, json; d = json.load(sys.stdin)["data"]; print("signed in as", d["username"], "with role", d["role"])'
Expected output:
signed in as admin@pmg with role admin

root@pam is Proxmox Mail Gateway's built-in superuser. Azure images keep the Linux root password locked, so root@pam cannot sign in, and admin@pmg is your way in. The admin role manages the whole mail filtering configuration; system tasks such as package upgrades are done over SSH with sudo.
Step 7: Check whether your subscription allows outbound port 25
if timeout 8 bash -c 'exec 3<>/dev/tcp/gmail-smtp-in.l.google.com/25' 2>/dev/null; then
echo "outbound TCP 25: open on this subscription"
else
echo "outbound TCP 25: blocked (use a port 587 smart host, see Step 10)"
fi
Step 8: Sign in to the web interface
- Browse to the
PMG_URLfrom the credentials file, for examplehttps://<public-ip>:8006/. The certificate is self signed and unique to this VM, so your browser shows a warning the first time. Step 12 explains how to install a trusted certificate. - Enter User name
adminand the password from the credentials file. - Set Realm to Proxmox Mail Gateway authentication server. The default, Linux PAM, is for
root@pamand will not accept this login.

- Proxmox Mail Gateway shows a No valid subscription notice to installations without a Proxmox subscription key. It is informational: this image uses the free no-subscription repository and every feature works. Select OK.
The dashboard shows mail volume, spam, processing time and resource usage:

Change the password now: go to Configuration > User Management > Local, select admin@pmg and choose Password. Add two factor authentication under Configuration > User Management > Two Factor.
Step 9: Prove spam and antivirus filtering work
Send the standard GTUBE spam test message through the gateway's external port and follow it in the log tracker. A result line ending :Q: means the message was quarantined:
FQDN=$(hostname -f)
START=$(date '+%F %T')
python3 - "$FQDN" <<'PY'
import smtplib, sys
fqdn = sys.argv[1]
gtube = "XJS*C4JDBQADN1.NSBN3*2IDNEN*GTUBE-STANDARD-ANTI-UBE-TEST-EMAIL*C.34X"
msg = f"From: test@{fqdn}\r\nTo: postmaster@{fqdn}\r\nSubject: GTUBE spam test\r\n\r\n{gtube}\r\n"
with smtplib.SMTP("127.0.0.1", 25) as s:
s.sendmail(f"test@{fqdn}", [f"postmaster@{fqdn}"], msg)
print("test message submitted")
PY
sleep 15
sudo pmg-log-tracker -s "$START"

In the web interface, Administration > Tracking Center lists the message with the status quarantine:

and Administration > Spam Quarantine, with the postmaster address selected under E-Mail, holds it with a spam score of 1000, ready to be delivered or deleted:

Now confirm the antivirus engine is loaded and detecting. The standard antivirus test string is streamed to ClamAV from memory, so nothing is written to disk:
clamdscan --version
printf '%s%s' 'X5O!P%@AP[4\PZX54(P^)7CC)7}$' 'EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*' | clamdscan --no-summary -
echo "clamdscan exit code: $? (1 means a threat was found)"
Expected output (the database number and date change daily):
ClamAV 1.4.4/28125/Wed Sep 16 06:24:23 2026
stream: Eicar-Test-Signature FOUND
clamdscan exit code: 1 (1 means a threat was found)
The rule system that made these decisions is under Mail Filter. The default rules block viruses and dangerous attachments, add spam headers and quarantine mail with a spam score of 3 or more:

Step 10: Route mail for your domain
A new gateway relays for no domains, so it rejects mail to anything but itself. Check the starting point:
postconf -h mynetworks relay_domains
sudo pmgsh get /config/domains

1. Add your domain and where to deliver it. In the web interface use Configuration > Mail Proxy > Relay Domains and Transports, or from SSH (replace the placeholders):
sudo pmgsh create /config/domains --domain <your-domain> --comment "filtered by this gateway"
sudo pmgsh create /config/transport --domain <your-domain> --host <your-mail-server> --port 25 --use_mx 0
2. Trust your internal mail servers so they can send outgoing mail through port 26 (the VM's own subnet is trusted automatically):
sudo pmgsh create /config/mynetworks --cidr <your-internal-cidr> --comment "internal mail servers for <your-domain>"
3. Send outbound mail through a smart host if your subscription blocks port 25 (see the port 25 section). Configuration > Mail Proxy > Relaying sets the smart host and port, or:
sudo pmgsh set /config/mail --smarthost smtp.<your-domain> --smarthostport 587
Proxmox Mail Gateway's smart host setting has no field for a username and password. For a relay that requires authentication (such as Azure Communication Services), either allow the gateway's public IP address on the relay provider's side, or add Postfix SASL settings through Proxmox Mail Gateway's custom configuration templates, as described under "Configuration files" in the Proxmox Mail Gateway administration guide.
4. Set the administrator e-mail address used for reports and notifications (select Configuration itself, then the Options tab, or):
sudo pmgsh set /config/admin --email postmaster@<your-domain>
Point your mail server's outgoing relay at the gateway's port 26 once the trusted network is in place, and only then change your MX record (Step 11).
Step 11: Host name and DNS
Mail servers judge a gateway by its name. The image names itself <vm-name>.<azure-dns-suffix> automatically; for production give it a public name in your domain:
sudo hostnamectl set-hostname mail
echo <your-domain> | sudo tee /etc/cloudimg/pmg-domain
sudo systemctl restart proxmox-mail-gateway-hostname.service
sudo pmgconfig sync --restart 1
hostname -f
Then publish in DNS:
- an A record for
mail.<your-domain>pointing at the VM's public IP address - an MX record for
<your-domain>pointing atmail.<your-domain> - an SPF record that includes the address mail is sent from
- a reverse DNS (PTR) name for the public IP: give the public IP a DNS name label and a reverse FQDN, for example
az network public-ip update --resource-group my-mail-rg --name pmg01PublicIP --dns-name pmg01 --reverse-fqdn mail.<your-domain>
Use a static public IP address so the MX and PTR records stay valid.
Step 12: TLS certificate, updates and subscription
Certificate. Under Configuration > Certificates you can upload certificates or request them with ACME (Let's Encrypt), separately for the web interface (API) and for SMTP. The HTTP challenge needs port 80 reachable while it runs; the DNS challenge needs no extra port.
Updates. The free pmg-no-subscription repository is configured and unattended-upgrades applies Debian security updates. To apply all Proxmox and Debian updates:
sudo apt-get update && sudo apt-get dist-upgrade
Subscription. The subscription only pmg-enterprise repository is present in /etc/apt/sources.list.d/pmg-enterprise.sources with Enabled: no. If you buy a subscription from Proxmox, enter the key under Configuration > Subscription and remove the Enabled: no line to switch to the enterprise repository.
Security recommendations
- Restrict ports 22, 26 and 8006 to the networks that need them (Step 3). Only port 25 needs to be open to the internet.
- Change the
admin@pmgpassword after the first sign-in and enable two factor authentication (Configuration > User Management). - Create separate accounts with the Quarantine Manager, Help Desk or Auditor roles instead of sharing the administrator (Configuration > User Management).
- Leave the default antivirus and spam rules enabled and review Statistics and the Tracking Center regularly.
- Keep the gateway patched (Step 12) and take configuration backups under Configuration > Backup/Restore.
Troubleshooting
systemctl is-active proxmox-mail-gateway-firstboot proxmox-mail-gateway-hostname
sudo mailq
sudo journalctl -u pmg-smtp-filter -n 20 --no-pager
- Login refused in the web interface: check the realm is Proxmox Mail Gateway authentication server and the user name is
admin. clamav-daemonnot active on the first minute: the signature download is still running; it starts automatically once signatures arrive (sudo journalctl -u clamav-freshclam -n 20 --no-pager).- Mail to your server stays in the queue: check the transport host and port, the NSG and routing between the gateway and your server, and whether it is an outbound port 25 connection that Azure blocks on your subscription (Step 7).
- Gateway services fail after a host name change: run
sudo systemctl restart proxmox-mail-gateway-hostname.serviceandsudo pmgconfig sync --restart 1.
Support
cloudimg provides 24/7 support for this image by email at support@cloudimg.co.uk. Commercial support and enterprise repository access for Proxmox Mail Gateway itself are available separately from Proxmox Server Solutions GmbH.
Licensing
Proxmox Mail Gateway is free and open source software licensed under the GNU Affero General Public License, version 3 or later; the source code is published by Proxmox at git.proxmox.com. Debian, Postfix, SpamAssassin, ClamAV and PostgreSQL are distributed under their own open source licences. ClamAV signature databases are downloaded by your VM directly from the ClamAV project.
Proxmox is a registered trademark of Proxmox Server Solutions GmbH. All other product and company names are trademarks or registered trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them.