Apache James
Overview
Apache James (Java Apache Mail Enterprise Server) is a long-established open source mail server from the Apache Software Foundation. It is a modular mail platform rather than a fixed product. It provides a full SMTP transfer agent for sending and receiving, IMAP mailbox access with STARTTLS and implicit TLS, Sieve filtering, and a programmable mailet container. The container lets you route, rewrite, filter and act on mail with ordinary Java logic.
This guide covers the deployment and operation of Apache James on Ubuntu 24.04 on Azure using SecureAnyCloud Azure Marketplace images. The image ships the free and open source, Apache 2.0 licensed James 3.9.0 in its single-node distribution. The mailbox store (JPA over embedded Apache Derby), the full-text search index (embedded Apache Lucene) and the mail queue (embedded Apache ActiveMQ) all run inside one JVM. There is no database server to operate, no search cluster and no message broker.
Nothing ships with a known credential. The image contains no mailbox, no TLS private key and none of the upstream default passwords. A unique TLS keystore and a unique postmaster password are generated on each VM at first boot, before the server accepts a connection.
Apache James and Apache are trademarks of The Apache Software Foundation. This image is produced by SecureAnyCloud and is not affiliated with, endorsed by, or sponsored by The Apache Software Foundation. It ships the free and open source Apache 2.0 licensed software, unmodified.
What you get:
- Apache James 3.9.0, the Apache 2.0 licensed single-node
jpa-guicedistribution, verified at build time by SHA-512 against Apache's published checksum. - SMTP on
25(inbound), ESMTP submission on465(implicit TLS) and587(STARTTLS), IMAP4 on143(STARTTLS) and993(implicit TLS). - Embedded Apache Derby 10.17.1 mailbox store, Apache Lucene 10.2.1 full-text search and Apache ActiveMQ 6.1.6 mail queue, all in one JVM.
- Sieve filtering and the programmable mailet container for custom routing and processing.
- A unique TLS keystore and a unique postmaster mailbox password generated per VM on first boot, never baked into the image.
- Authentication required for every relay. The upstream sample configuration that relays for anything on the local machine is removed.
- The
webadminREST administration API bound to loopback only, with thejames-adminhelper for everyday operations. - OpenJDK 21 headless runtime from the Ubuntu archive.
apache-james.serviceandapache-james-firstboot.serviceas systemd units, enabled on boot.- Ubuntu 24.04 LTS base with the latest security patches applied at build time and unattended security upgrades enabled.
- Azure Linux Agent for cloud integration and SSH key injection.
Before you start: Azure blocks outbound port 25
This is the most important thing to know before you deploy any mail server on Azure.
Microsoft blocks outbound TCP port 25 from Azure virtual machines. Per Microsoft's current guidance:
- Enterprise Agreement (EA) and Microsoft Customer Agreement for enterprise (MCA-E) subscriptions are not blocked.
- Enterprise Dev/Test subscriptions are blocked by default but can be exempted. Use the virtual network's Diagnose and solve problems page and the Cannot send email (SMTP-Port 25) self-service flow.
- All other subscription types are blocked with no exemption available. These are pay as you go, MSDN, CSP, Free Trial, Azure Pass, Azure in Open and Education.
What this means in practice for this image:
Works on every Azure subscription
Receiving mail on port 25 from other mail servers Yes, inbound is completely unaffected
IMAP mailbox access on 143 and 993 Yes
Authenticated submission from your mail clients on 587 and 465 Yes
Sending by connecting directly to recipient MX servers on port 25 Only on EA / MCA-E
Sending through an authenticated smart host relay on port 587 Yes, Microsoft's own recommendation
As shipped, this is a fully functional receiving and IMAP mail server on any subscription. For outbound delivery to the public internet, configure a relay. This is not a limitation of Apache James or of this image. It is an Azure platform policy that applies to every VM-based mail server. Do not expect direct-to-MX delivery to work unless you are on EA or MCA-E.
The measurement below was taken on a real Azure VM running this image, not quoted from documentation:
Prerequisites
- An Azure subscription with permission to deploy virtual machines.
- An SSH key pair for VM access.
- A network security group allowing your mail ports inbound (see below) and TCP 22 from your management address.
- Recommended size:
Standard_B2s(2 vCPU, 4 GiB). The JVM heap is deliberately capped at 1 GiB so the server fits this size with headroom and without swap, which Azure image certification does not permit. For heavy mailbox volume, move up toStandard_B2msor larger.
Network security group rules
Open only the ports you intend to serve:
Port Protocol Purpose Open to
22 TCP SSH administration Your management address only
25 TCP Inbound SMTP from other mail servers The internet, if you receive external mail
587 TCP Authenticated submission, STARTTLS Your users' networks
465 TCP Authenticated submission, implicit TLS Your users' networks
143 TCP IMAP4, STARTTLS Your users' networks
993 TCP IMAP4, implicit TLS Your users' networks
Never open port 8000 (the webadmin administration API). It is bound to loopback and has no authentication. Reach it over an SSH tunnel, which makes your SSH key the front door.
Deploying the image
Launch the image from the Azure Marketplace as you would any VM. Choose Standard_B2s or larger and supply your SSH public key. The admin user is azureuser.
First boot runs apache-james-firstboot.service. It generates this VM's TLS keystore and postmaster password, creates the default mail domain and the postmaster mailbox, and starts the server. It typically completes in under a minute.
Until first boot has finished, apache-james.service deliberately does not start. It is gated on a marker that first boot writes only after every per-VM secret is on disk, so the server can never come up against the shipped template configuration.
Connect over SSH:
ssh azureuser@<vm-ip>Confirming the server is running
Check both services:
systemctl is-active apache-james.service
systemctl show -p ActiveState --value apache-james-firstboot.serviceapache-james.service reports active. apache-james-firstboot.service reports inactive once it has completed. It is a one-shot unit that disables itself after a successful run, which is the result you want to see.
Check the release that is running and the ports it has published:
unzip -p /opt/james/james-server-jpa-app.jar META-INF/MANIFEST.MF | tr -d '\r' | grep -E '^(Implementation-Version|Main-Class):' ss -lntH '( sport = :25 or sport = :143 or sport = :465 or sport = :587 or sport = :993 )' | awk '{print $4}' | sort -uImplementation-Version: 3.9.0 and Main-Class: org.apache.james.JPAJamesServerMain confirm this is James 3.9.0 in its single-node form, and all five mail ports are bound.
Ask the server whether it is healthy:
curl -fsS http://127.0.0.1:8000/healthcheck | head -c 300; echoYou get a JSON document beginning {"status":"healthy" with a checks array covering the Guice application lifecycle, mail reception and the event dead letter store.
Your per-VM credentials
Every VM generates its own secrets at first boot. They are written to a file that only root can read. Inspect the file:
sudo stat -c '%A %U:%G %n' /root/apache-james-credentials.txt
sudo grep -E '^JAMES_(MAIL_DOMAIN|POSTMASTER_ADDRESS|WEBADMIN_URL)=' /root/apache-james-credentials.txtThe file is -rw------- root:root. It records:
Key What it is
JAMES_MAIL_DOMAIN The default mail domain created for this VM
JAMES_POSTMASTER_ADDRESS The one mailbox that exists, postmaster@ that domain
JAMES_POSTMASTER_PASSWORD That mailbox's password, generated on this VM
JAMES_KEYSTORE_SECRET The password protecting this VM's TLS keystore
JAMES_DB_USER / JAMES_DB_PASSWORD The embedded datastore credentials, rotated per VM
JAMES_SUBMISSION_ENDPOINT / JAMES_IMAPS_ENDPOINT Where to point a mail client
To read the postmaster password, run:
sudo grep -E '^JAMES_POSTMASTER_PASSWORD=' /root/apache-james-credentials.txt | cut -d= -f2-These values exist only on this VM. Two VMs launched from this image generate completely different secrets. None of the upstream Apache James sample passwords, including the widely published default TLS keystore password, will open anything here.
Proving mail actually flows
The image ships a self-test that does a genuine end-to-end round trip. It then checks that the things which must not work really do not. Run it:
sudo /usr/local/sbin/james-selftest.shThe self-test authenticates as the per-VM postmaster and submits a message on port 587 with STARTTLS and SMTP AUTH, carrying a random nonce. It polls IMAP on 993 until the message is genuinely delivered, fetches it and asserts that the exact nonce is in the body. Then it logs in again on 143 with STARTTLS.
It then confirms that an unauthenticated relay attempt is refused, a wrong submission password is refused and a wrong IMAP password is refused. As a positive control, it confirms that unauthenticated inbound mail to a local recipient is still accepted. It removes its own probe message, so it leaves nothing behind and is safe to run at any time.
Look for the open-relay line:
open-relay attempt REJECTED as required (RCPT TO refused 550 '5.7.1 Requested action not taken: relaying denied')This server is not an open relay
An open relay is a mail server that will forward mail to any destination for anyone. It is the most serious defect a mail server can ship with, and it gets your address space blacklisted within hours.
Apache James's own sample configuration sets <authorizedAddresses>127.0.0.0/8</authorizedAddresses>. That permits any process on the machine to relay to any destination with no authentication. It is a convenience default for a developer laptop and it is wrong for an internet-facing server, so this image removes it entirely. SMTP AUTH is required before anything is relayed anywhere, from any source address including loopback.
Receiving mail is unaffected. James rejects an unauthenticated sender only when the MAIL FROM address is in one of your domains, which is anti-spoofing. Ordinary inbound mail from other servers to your users is accepted normally. The self-test above proves both behaviors.
To confirm the setting is absent, run:
sudo grep -c authorizedAddresses /var/lib/james/conf/smtpserver.xml || echo "0 - relay requires authentication"Administering the server
Apache James's Guice server has no web interface. You administer it through the webadmin REST API. This image binds that API to 127.0.0.1:8000 only, because in this configuration the API has no authentication of its own. Your SSH key is the authenticated front door.
The image bundles james-admin, a small wrapper over that API, so ordinary operations are one command. Run it with no arguments to print the usage:
james-adminThe everyday commands are:
james-admin health james-admin domains james-admin usersAdding your own domain and mailboxes
Add the domain you receive mail for, then create mailboxes in it:
sudo james-admin add-domain example.net
sudo james-admin add-user alice@example.net 'ChangeThisToAStrongPassword' james-admin domains james-admin usersRun add-user with no password argument to generate a strong password and print it.
Address length limit. This distribution stores domains and mailbox addresses in columns capped at 100 characters each. A mailbox address longer than that is rejected, and a domain longer than that is silently ignored. Ordinary addresses are nowhere near the limit, but keep it in mind if you generate addresses programmatically. james-admin checks the length and reports it plainly instead of letting the API return an opaque error.
To change a password or remove an account later, run:
sudo james-admin set-password alice@example.net 'AnotherStrongPassword'
sudo james-admin remove-user alice@example.net
sudo james-admin remove-domain example.netReaching the API from your workstation
Open an SSH tunnel to use the API from your workstation. The API stays unexposed to the network:
ssh -L 8000:127.0.0.1:8000 azureuser@<vm-ip>From your workstation, you can then reach http://127.0.0.1:8000/domains, /users, /healthcheck and the rest of the webadmin API. Close the tunnel when you are done.
Connecting a mail client
Point any standards-compliant mail client at the VM's public address:
Setting Value
Incoming (IMAP) Your VM's address, port 993, SSL/TLS
Incoming (IMAP, alternative) Your VM's address, port 143, STARTTLS
Outgoing (SMTP submission) Your VM's address, port 587, STARTTLS
Outgoing (alternative) Your VM's address, port 465, SSL/TLS
Username The full mailbox address, for example alice@example.net
Password The mailbox password you set
Authentication Normal password
The image generates a self-signed certificate for the VM's own name, so your client warns about the certificate on first connection until you install a real one. See the next section.
Installing a real TLS certificate
Replace the self-signed keystore with one built from a certificate issued for the name your users connect to. Obtain the certificate the way you normally do. Then import the certificate and key into a PKCS12 keystore and point James at it.
Convert a PEM certificate and key into a PKCS12 keystore with the alias James expects:
sudo openssl pkcs12 -export \
-in /path/to/fullchain.pem \
-inkey /path/to/privkey.pem \
-name james \
-out /var/lib/james/conf/keystore.new \
-passout pass:YOUR_NEW_KEYSTORE_PASSWORDUpdate the keystore password recorded in each protocol configuration, swap the file in and restart:
sudo sed -i 's|<secret>.*</secret>|<secret>YOUR_NEW_KEYSTORE_PASSWORD</secret>|g' \
/var/lib/james/conf/smtpserver.xml /var/lib/james/conf/imapserver.xml
sudo mv /var/lib/james/conf/keystore.new /var/lib/james/conf/keystore
sudo chown james:james /var/lib/james/conf/keystore
sudo chmod 0640 /var/lib/james/conf/keystore
sudo systemctl restart apache-james.serviceKeep the keystore password out of your shell history, and renew the certificate before it expires.
Sending mail out through a relay
Azure blocks outbound port 25 on most subscriptions, so configure an authenticated smart host on port 587. Apache James does this in mailetcontainer.xml. The RemoteDelivery mailet of the relay processor takes a gateway, gatewayPort and credentials. It then sends everything through your provider instead of connecting to recipient MX servers directly.
Add the gateway settings to the RemoteDelivery mailet:
<mailet match="All" class="RemoteDelivery"> <outgoingQueue>outgoing</outgoingQueue> <delayTime>5000, 100000, 500000</delayTime> <maxRetries>3</maxRetries> <maxDnsProblemRetries>0</maxDnsProblemRetries> <deliveryThreads>10</deliveryThreads> <sendpartial>true</sendpartial> <bounceProcessor>bounces</bounceProcessor> <gateway>smtp.your-relay-provider.net</gateway> <gatewayPort>587</gatewayPort> <gatewayUsername>your-relay-username</gatewayUsername> <gatewayPassword>your-relay-password</gatewayPassword> <startTLS>true</startTLS> </mailet>Edit /var/lib/james/conf/mailetcontainer.xml, then restart the service and check health:
sudo systemctl restart apache-james.service
sudo james-admin healthYour relay provider tells you which SPF, DKIM and DMARC DNS records to publish. Publish them. Without them, most large providers reject or quarantine your mail regardless of which server sent it.
DNS records for receiving mail
To receive mail for a domain you have added, publish these records:
- An MX record for the domain, pointing at a hostname that resolves to this VM's public IP.
- An A record for that hostname, pointing at the VM's public IP.
- A PTR (reverse DNS) record for the public IP, pointing back at that hostname. Configure it on the Azure public IP resource.
Give the VM a static public IP. A dynamic address changes across a stop and deallocate, and your MX record silently stops resolving to your server.
Where everything lives
List the two main directories:
ls /opt/james
ls /var/lib/jamesPath Contents
/opt/james The Apache James distribution: application jar, dependency jars, and conf.template, the pristine template configuration. Owned by root, read-only to the service.
/var/lib/james/conf The live configuration this VM is running
/var/lib/james/var/store The embedded Derby mailbox database
/var/lib/james/var/mail Mail repositories: error, relay denied, address error queues
/var/lib/james/var/log james.log and its rotated archives
/var/lib/james The Lucene full text search index, written directly into the working directory
/root/apache-james-credentials.txt This VM's generated credentials, mode 0600
Logs and troubleshooting
Read the server log and the service status:
sudo tail -30 /var/lib/james/var/log/james.log
sudo systemctl status apache-james.service --no-pager | head -20If the server does not start, the JVM's own output goes to the journal:
sudo journalctl -u apache-james.service -n 60 --no-pager | tail -30If apache-james.service reports inactive (dead) with condition failed, first boot has not completed. Check first boot:
sudo systemctl status apache-james-firstboot.service --no-pager | head -20 sudo tail -20 /var/log/sac/sac-firstboot.logBackups
Everything stateful is under /var/lib/james. Stop the server for a consistent copy of the embedded database, archive the directory, then start it again:
sudo systemctl stop apache-james.service
sudo tar -czf /root/james-backup-$(date +%F).tar.gz -C /var/lib james
sudo systemctl start apache-james.serviceAzure managed disk snapshots of the OS disk are the simplest whole-machine option, and Azure Backup can schedule them.
Updating
Ubuntu security updates are applied automatically by unattended-upgrades, which is enabled in the image. Verify it:
systemctl is-enabled unattended-upgrades.service grep 'Unattended-Upgrade' /etc/apt/apt.conf.d/20auto-upgradesFor Apache James itself, SecureAnyCloud publishes refreshed images as upstream releases. Deploy the new image alongside the old one, migrate mail with your usual IMAP tooling, then retire the old VM. This keeps the mailbox store and the application version in step.
Licensing
Apache James is free and open source software licensed under the Apache License 2.0, and is shipped here unmodified. The bundled runtime components (Apache Derby, Apache Lucene, Apache ActiveMQ, Netty, Jetty and Guice) are likewise permissively licensed. The Java runtime is Ubuntu's own openjdk-21-jre-headless package, installed from the Ubuntu archive. The full licence text ships in the image:
head -5 /opt/james/LICENSEThe SecureAnyCloud charge is for the packaging, hardening, testing and support of the image, not for the software itself.
Support
SecureAnyCloud provides 24/7 support for this image. Contact support through the Azure Marketplace listing or at secureanycloud.com.
Deploy on Azure
Launch Apache James on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.
View this image
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com