Mailtrain on Ubuntu 24.04 on Azure User Guide
Overview
Mailtrain is a self hosted newsletter and email marketing platform for building subscriber lists, designing campaigns and tracking delivery, all from your own infrastructure. It gives you list management with custom fields and segments, drag and drop email template editors (Mosaico and GrapesJS), subscription and unsubscription forms, regular, RSS and automation triggered campaigns, and per campaign reporting. The cloudimg image ships Mailtrain 2 running on Node.js under systemd on a hardened, fully patched Ubuntu 24.04 LTS base, fronted by nginx, together with a bundled local MariaDB database and a Redis session cache so the appliance is complete and useful on its own.
Mailtrain runs three separate web endpoints, all served through nginx: a trusted admin interface on port 80, a sandbox endpoint on port 8003 for the template editors, and a public endpoint on port 8004 for subscription forms and the campaign archive. The bundled MariaDB and Redis listen only on the loopback interface and are never exposed. A unique administrator password and API access token are generated on the first boot of every VM. Backed by 24/7 cloudimg support.
What is included:
- Mailtrain 2 running on Node.js 14 under systemd (
mailtrain.service), fronted by nginx - A bundled MariaDB server holding the Mailtrain database, already installed, migrated and ready
- A bundled Redis session cache
- Three nginx fronted endpoints: trusted admin UI on
:80, sandbox editors on:8003, public subscription and archive on:8004 - A per VM administrator password and REST API access token generated on first boot and recorded in a root only file
- No shipped default login: Mailtrain's default
admin/testaccount is replaced with a unique per VM secret, and no known or blank credential authenticates - MariaDB and Redis bound to
127.0.0.1only, never exposed to the network - Two example subscriber lists so the interface is ready to explore
- An unauthenticated
/healthzendpoint for Azure Load Balancer health probes - 24/7 cloudimg support
Prerequisites
An active Azure subscription, an SSH key pair, and a VNet plus subnet in the target region. Standard_B2s (2 vCPU / 4 GiB RAM) is a sensible starting point; size up for larger lists and higher send volumes. NSG inbound: allow 22/tcp from your management network, and 80/tcp, 8003/tcp and 8004/tcp for the three Mailtrain endpoints. Mailtrain serves plain HTTP; for production, terminate TLS in front of it with your own domain. The bundled MariaDB and Redis are never exposed: they listen on 127.0.0.1 only.
Step 1 - Deploy from the Azure Marketplace
Sign in to the Azure Portal, choose Create a resource, search the Marketplace for Mailtrain by cloudimg, and select Create. On Basics pick your subscription, resource group, region and size; under Administrator account choose SSH public key and paste your key; under Inbound port rules allow SSH (22) and HTTP (80). After creation, also open ports 8003 and 8004 (Step 2) so the sandbox and public endpoints are reachable. Then Review + create then Create.
Step 2 - Deploy from the Azure CLI
az vm create \
--resource-group <your-rg> \
--name mailtrain \
--image <marketplace-image-urn> \
--size Standard_B2s \
--admin-username azureuser \
--generate-ssh-keys \
--public-ip-sku Standard
Then open the three Mailtrain endpoints:
az vm open-port --resource-group <your-rg> --name mailtrain --port 80 --priority 900
az vm open-port --resource-group <your-rg> --name mailtrain --port 8003 --priority 910
az vm open-port --resource-group <your-rg> --name mailtrain --port 8004 --priority 920
Step 3 - Confirm the services are running
SSH in as azureuser and confirm Mailtrain, nginx, MariaDB and Redis are all active. Note that nginx serves the three endpoints on ports 80, 8003 and 8004 while MariaDB and Redis stay on the loopback interface.
systemctl is-active mailtrain nginx mariadb redis-server mailtrain-firstboot
sudo ss -tlnp | grep -E ':80 |:8003 |:8004 |:3306 |:6379 ' | sed 's/ */ /g'

Step 4 - Retrieve the per VM administrator password
Every VM generates its own Mailtrain administrator password and REST API access token on first boot and writes them, along with the login user and the three endpoint URLs, to a root only credentials file. Read it with sudo:
sudo cat /root/mailtrain-credentials.txt

You sign in to the admin UI as user admin with the MAILTRAIN_ADMIN_PASSWORD from this file. The MAILTRAIN_ACCESS_TOKEN authenticates the REST API.
Step 5 - Sign in to the admin interface
Browse to http://<vm-public-ip>/. You land on the Mailtrain sign in page. Enter username admin and the password from Step 4, then click Sign in.

Step 6 - Manage subscriber lists
After signing in you land on Lists. The image ships with two example lists so you can explore straight away. Each row shows the list name, its identifier, the subscriber count and namespace. Use Create List to add your own, and the row actions to manage subscribers, fields, segments, forms and imports.

Step 7 - Manage subscribers
Open a list to reach its Subscribers view, where you can add subscribers, import them from CSV, search and segment them, and export. The example list below is populated so you can see the layout, the per subscriber status and the sub navigation for Fields, Segments, Imports and Triggers.

Step 8 - Build a campaign
Open Campaigns then Create Campaign to build a regular newsletter. You give it a name, choose the list or segment to send to, and pick or design a template with the Mosaico or GrapesJS editors. The campaign builder is shown below.

Step 9 - Connect a send configuration to send email
Mailtrain manages your lists, templates and campaigns out of the box, and it sends email through a Send Configuration that you point at your own mail service. Open Administration then Send Configurations and add one for your SMTP relay or Amazon SES account (host, port, username and password). Once a send configuration exists you can send test messages and launch campaigns. This keeps deliverability under your control and lets you use your own authenticated sending domain (SPF, DKIM and DMARC).
Step 10 - No known or default credentials
Mailtrain normally ships a default admin / test account. The cloudimg image removes it: the administrator password is rotated to a unique per VM secret on first boot, and no known or blank credential authenticates. You can prove this with the built in round-trip check, which confirms the per VM API access token authenticates over HTTP while a missing or wrong token is rejected, and that the rotated administrator password rejects the default test and other weak guesses:
sudo mariadb -N -e "SELECT CONCAT(username,' (admin account present)') FROM mailtrain.users WHERE username='admin'"
sudo bash /usr/local/sbin/mailtrain-cred-roundtrip.sh

Step 11 - Verify the stack and where your data lives
Confirm the Node.js and Mailtrain versions, the installed schema, the example lists and that the built in ZoneMTA is disabled so no MongoDB is required. The Mailtrain database lives in the bundled MariaDB, and the Redis session cache and MariaDB both stay on the loopback interface, so all of your data is kept on the VM and off the network:
printf 'node %s | Mailtrain %s\n' "$(/opt/node/bin/node -v)" "$(/opt/node/bin/node -e "console.log(require('/opt/mailtrain/server/package.json').version)")"
sudo mariadb -N -e "SELECT CONCAT('schema tables: ', COUNT(*)) FROM information_schema.tables WHERE table_schema='mailtrain'; SELECT CONCAT(name, ' -> ', subscribers, ' subscribers') FROM mailtrain.lists ORDER BY id"
curl -sI http://127.0.0.1/healthz | head -1

The three Mailtrain endpoints
Mailtrain deliberately separates its interfaces so that untrusted content (subscriber submitted data, template previews) is served from a different origin than the admin UI:
- Trusted (
http://<vm-public-ip>/, port 80) - the admin interface where you manage lists, campaigns, templates and settings. - Sandbox (
http://<vm-public-ip>:8003/, port 8003) - serves the Mosaico and GrapesJS template editors in an isolated origin. - Public (
http://<vm-public-ip>:8004/, port 8004) - serves subscription and unsubscription forms and the campaign archive to your recipients.
All three are set to your VM's public IP automatically on first boot. Make sure ports 80, 8003 and 8004 are open in your NSG.
Adding your own domain and TLS
The appliance serves plain HTTP, and Mailtrain bakes its endpoint URLs into the emails and forms it serves, so for production you should front it with your own domain and TLS. Terminate TLS with Azure Application Gateway, an nginx or Caddy reverse proxy, or a managed load balancer with a certificate for your domain, then update the trustedUrlBase, sandboxUrlBase and publicUrlBase values in /opt/mailtrain/server/config/production.yaml to your public addresses and restart Mailtrain:
sudo systemctl restart mailtrain
Security notes
- Mailtrain serves plain HTTP. For anything beyond a trusted network, put it behind your own TLS terminating reverse proxy or Azure Application Gateway with a certificate for your domain.
- The bundled MariaDB and Redis listen on
127.0.0.1only and are never reachable from the network. Administer the database over SSH withsudo mariadb. - The administrator password and API access token are unique per VM and no known or blank credential authenticates. Keep
/root/mailtrain-credentials.txtprotected and change the password from within Mailtrain if you share access. - Restrict inbound
80/tcp,8003/tcpand8004/tcpin your NSG to the networks that need them.
Support
This image is maintained by cloudimg with 24/7 support. If you need help deploying or operating Mailtrain on Azure, contact us at cloudimg.co.uk.