D1
Security Azure

Debian 13 VM with Proxmox Mail Gateway 9 on Azure User Guide

| Product: Debian 13 VM with Proxmox Mail Gateway 9 on Azure

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 only root can 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-subscription update 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

  1. Open the Debian 13 VM with Proxmox Mail Gateway 9 listing by cloudimg in the Azure Marketplace and select Create.
  2. Choose a resource group, region and a VM size (see Prerequisites).
  3. Set the administrator username (for example azureuser) and paste your SSH public key.
  4. On Networking, allow inbound 22, 25, 26 and 8006 (Step 3 explains what each is for and how to restrict them).
  5. 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:

All Proxmox Mail Gateway services active, pmgversion 9.1.2 on the Debian cloud kernel, listeners on 22, 25, 26 and 8006

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

Credentials file 0600 root:root with the password masked; API sign-in as admin@pmg with role admin; a wrong password refused with HTTP 401; only root@pam and admin@pmg exist

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

  1. Browse to the PMG_URL from the credentials file, for example https://<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.
  2. Enter User name admin and the password from the credentials file.
  3. Set Realm to Proxmox Mail Gateway authentication server. The default, Linux PAM, is for root@pam and will not accept this login.

Proxmox Mail Gateway login window with the user admin and the Proxmox Mail Gateway authentication server realm selected

  1. 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:

Proxmox Mail Gateway 9.1.2 dashboard signed in as admin@pmg, showing e-mail volume and spam charts

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"

GTUBE test message submitted on port 25 and reported by pmg-log-tracker with status Q, quarantined

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

Tracking Center listing the GTUBE test messages with 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:

Spam Quarantine for the postmaster address holding the GTUBE test messages with score 1000

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:

Mail Filter rules list with Block Viruses, Block Dangerous Files, Modify Header, Welcomelist and Quarantine/Mark Spam (Level 3) active

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

ClamAV version and the antivirus test stream FOUND; trusted networks are the VM subnet and loopback; relay domains empty; no-subscription repository enabled and enterprise repository disabled

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 at mail.<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@pmg password 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-daemon not 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.service and sudo 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.