Inbucket on Ubuntu 24.04 on Azure User Guide
Overview
Inbucket is the popular open source, self-hosted disposable email server for development and testing. It runs an SMTP sink that accepts mail for any address, then lets you read and verify every captured message in a web interface and over a REST API — so you can assert exactly what an application sent without ever delivering real email.
Inbucket is a single Go binary that serves the web UI, the REST API, an SMTP sink and an optional POP3 server. It keeps messages in its own on-disk store, so there is no external database to run.
By design, Inbucket has no authentication of its own and its web UI is world-readable to anyone who can reach it. The cloudimg image makes it secure by default: the web UI and REST API bind to loopback and are published on port 80 by nginx behind HTTP Basic Auth, whose password is generated uniquely on the first boot of every VM. Two VMs deployed from the same image never share a password, and there is no default login.

What is included:
- Inbucket 3.1.1 (single Go binary, bundles the web UI) at
/opt/inbucket/inbucket - nginx reverse proxy on
:80with per-VM HTTP Basic Auth in front of the Inbucket web UI + REST API on loopback:9000 - The SMTP sink on
0.0.0.0:2500, ready to receive test mail - POP3 bound to loopback
127.0.0.1:1100(reachable over an SSH tunnel) - Inbucket's on-disk file store at
/var/lib/inbucket(72-hour retention, 500 messages per mailbox) - A per-VM web password generated at first boot, in a root-only file, with no default login
inbucket.serviceandnginx.serviceas systemd units, enabled and active- A shipped self-test,
/usr/local/sbin/inbucket-selftest.sh, that sends a message through the sink and retrieves it through the API - A fully patched Ubuntu 24.04 LTS security baseline at capture time, with unattended security updates enabled
- 24/7 cloudimg support
Understanding the access model
This image is deliberately locked down because Inbucket itself has no login and captured mail can be sensitive. Three listeners, three different exposures:
- Web UI + REST API (port 80). Served by nginx with per-VM HTTP Basic Auth. Safe to reach over the network — a caller must supply the per-VM credential before it sees any mail. Inbucket's own web port (
9000) is bound to loopback and never exposed directly. - SMTP sink (port 2500). This is the product's purpose: your application posts test mail here. It accepts mail with no authentication (that is how test mail servers work), so it ships closed at the network boundary — you open port 2500 deliberately, only to the senders you trust (see Step 9). Never expose an open SMTP sink to the whole internet.
- POP3 (port 1100). Bound to loopback only. Reach it over an SSH tunnel if you use POP3 (see Step 10); it is never published to the network.
Prerequisites
An active Azure subscription, an SSH key pair, and a VNet + subnet in the target region. Standard_B2s (2 vCPU / 4 GiB RAM) is a good starting point — Inbucket is lightweight. NSG inbound: allow 22/tcp from your management network and 80/tcp from the networks that will view captured mail. Open 2500/tcp only from the specific hosts or subnets that should be allowed to post mail to the sink, and leave POP3 (1100) closed.
Step 1 — Deploy from the Azure Marketplace
Sign in to the Azure Portal, choose Create a resource, search the Marketplace for Inbucket 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). Then Review + create → Create. First boot initialisation takes about a minute after the VM starts. Add an inbound rule for 2500/tcp from your trusted senders once the VM is up.
Step 2 — Deploy from the Azure CLI
az vm create \
--resource-group <your-rg> \
--name inbucket \
--image <marketplace-image-urn> \
--size Standard_B2s \
--admin-username azureuser \
--ssh-key-values ~/.ssh/id_ed25519.pub \
--vnet-name <your-vnet> --subnet <your-subnet> \
--public-ip-sku Standard
# Web UI, reachable by anyone on this network but gated by per-VM basic auth:
az vm open-port --resource-group <your-rg> --name inbucket --port 80 --priority 1010
# SMTP sink: open 2500 ONLY to the senders you trust (replace the source prefix):
az network nsg rule create --resource-group <your-rg> --nsg-name inbucketNSG \
--name allow-smtp-sink --priority 1020 --access Allow --protocol Tcp \
--destination-port-ranges 2500 --source-address-prefixes <your-app-server-cidr>
Step 3 — Connect to your VM
ssh azureuser@<vm-public-ip>
Step 4 — Verify the Inbucket stack
Confirm both services are active and check the port bindings — only 80 and 2500 listen on all interfaces; the web port 9000 and POP3 1100 are bound to loopback:
systemctl is-active inbucket nginx
ss -tln | grep -E ':(80|2500|9000|1100) ' || sudo ss -tln | grep -E ':(80|2500|9000|1100) '
Both lines read active; ports 9000 and 1100 show 127.0.0.1, while 80 and 2500 listen on 0.0.0.0.

Step 5 — Retrieve your web sign-in
The web password generated on the first boot of your VM is stored in a root-only file, alongside the URL and the SMTP sink details:
sudo cat /root/inbucket-credentials.txt
Store the password in your password manager and treat the file as sensitive. The username is admin.
Step 6 — Confirm the web UI is authenticated
The front door rejects anonymous and wrong-password requests and accepts the generated password. The first two commands report 401 and the third 200:
curl -s -o /dev/null -w 'anonymous HTTP %{http_code}\n' http://127.0.0.1/
curl -s -o /dev/null -w 'wrong password HTTP %{http_code}\n' -u admin:wrong-password http://127.0.0.1/
PASS=$(sudo grep '^inbucket.web.pass=' /root/inbucket-credentials.txt | cut -d= -f2-)
curl -s -o /dev/null -w 'per-VM password HTTP %{http_code}\n' -u admin:$PASS http://127.0.0.1/

Step 7 — Sign in and view captured mail
Open a web browser and navigate to http://<vm-public-ip>/. Your browser prompts for the HTTP Basic Auth credential — sign in with the username admin and the password from /root/inbucket-credentials.txt. To view a mailbox, type the local part of an address (for example demo for demo@inbucket.local) into the box at the top right and press View. Every message sent to that address is listed, newest first.

Click a message to open it. Inbucket renders the HTML body (sandboxed), the plain-text alternative, the parsed headers and the raw source, along with any attachments — everything your application actually sent.

Step 8 — Send a test message and read it back
Point any SMTP client at the sink on port 2500. From the VM itself you can send one with Python, then read it back through the authenticated REST API:
python3 - <<'PY'
import smtplib
from email.message import EmailMessage
m = EmailMessage()
m["From"] = "app@example.test"
m["To"] = "welcome@inbucket.local"
m["Subject"] = "Verify your email"
m.set_content("Your verification code is 550132.")
s = smtplib.SMTP("127.0.0.1", 2500, timeout=15)
s.send_message(m)
s.quit()
print("sent")
PY
Because mailbox naming is by local part, the message lands in the welcome mailbox. Retrieve it over the REST API (which is behind the same basic auth) — the list endpoint returns message summaries, and the show endpoint returns the full body:
PASS=$(sudo grep '^inbucket.web.pass=' /root/inbucket-credentials.txt | cut -d= -f2-)
ID=$(curl -s -u admin:$PASS http://127.0.0.1/api/v1/mailbox/welcome | jq -r '.[0].id')
curl -s -u admin:$PASS http://127.0.0.1/api/v1/mailbox/welcome/$ID | jq '{from, subject, body: .body.text}'
A shipped self-test does the whole round-trip for you — it sends a uniquely tagged message through the sink and asserts the API returns the matching body:
sudo bash /usr/local/sbin/inbucket-selftest.sh

Step 9 — Point your application at the SMTP sink
In your application or test suite, set the SMTP host to your VM's address and the port to 2500, with no TLS and no authentication:
SMTP host: <vm-public-ip>
SMTP port: 2500
From another host, mail only reaches the sink if you opened 2500/tcp in the NSG from that source (Step 2). Keep that rule scoped to the specific senders that should be allowed to post — an SMTP sink open to the whole internet is a spam and abuse magnet. Inbucket accepts and stores everything it receives, so it is ideal for CI: run your application against it, then assert over the REST API that the expected mail arrived.
Step 10 — POP3 access over an SSH tunnel (optional)
POP3 is bound to loopback and never exposed to the network. If a tool needs to fetch messages over POP3, forward the port over SSH from your workstation:
ssh -L 1100:127.0.0.1:1100 azureuser@<vm-public-ip>
# then point your POP3 client at localhost:1100 (mailbox = the local part of the address)
Security baseline
The image is captured with the Ubuntu 24.04 security baseline fully applied (including phased updates) and ships with unattended security upgrades enabled, so your VM keeps patching itself after deployment:
cat /etc/apt/apt.conf.d/20auto-upgrades

Enabling HTTPS
For any use over an untrusted network, serve the web UI over HTTPS so the basic-auth credential and captured mail are encrypted in transit. Point a DNS A record at the VM's public IP, then install a certificate with Certbot (replace the domain):
sudo apt-get update && sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d your-domain.example.com
After enabling HTTPS, open 443/tcp in the NSG and restrict or close inbound port 80.
Maintenance and message retention
Inbucket keeps messages in its file store at /var/lib/inbucket, retaining them for 72 hours and capping each mailbox at 500 messages; older messages are removed automatically. To change these limits, edit /etc/inbucket/inbucket.env (for example INBUCKET_STORAGE_RETENTIONPERIOD and INBUCKET_STORAGE_MAILBOXMSGCAP) and restart with sudo systemctl restart inbucket. Apply operating system security updates with sudo apt update && sudo apt upgrade; the service restarts cleanly. To rotate the web password yourself, run sudo htpasswd -b /etc/nginx/inbucket.htpasswd admin '<new-password>' and sudo systemctl reload nginx, then record it in /root/inbucket-credentials.txt.
Support
This image is backed by 24/7 cloudimg support. Contact us by email and chat for deployment help, integration with your test suites, TLS termination and network scoping of the SMTP sink. Inbucket itself is open source software licensed under the MIT license.
All 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.