Deploy Poste.io | (Just Updated) Self-Hosted Mail Server with Webmail, Admin Preset, Data Survives Redeploys
Poste.io mail server: SMTP, IMAP, webmail, spam filter and REST API
poste
Just deployed
/data
Deploy and Host Poste.io on Railway
Built for an LLM agent to deploy and use. There is no install wizard to open: the admin account is created from a generated password on first boot, and the contract below lists every address, variable name and one script that creates a mailbox over the REST API, sends a message through SMTP submission, reads it back over IMAP and removes the mailbox.
Poste.io is an open source, all-in-one mail server. One container runs the SMTP server (Haraka), the IMAP server (Dovecot), the Rspamd spam filter, a webmail client, an admin panel and a REST API for domains and mailboxes. This listing packages the free edition, 2.5.22, with the install wizard answered for you.
About Hosting Poste.io
This template runs one service: Poste.io 2.5.22 in a wrapper image, pinned by digest, with a volume on /data. The volume holds mail, mailbox and domain data, the admin account and the server configuration, so everything survives redeploys. The admin user is admin@ plus your service's public domain, and its password is generated per deploy.
The wrapper image does four things: it serves the web interface as plain HTTP on Railway's injected PORT (the Railway edge terminates TLS), it answers the install wizard once from variables, it turns ClamAV off (it alone needs over a gigabyte of memory; Rspamd stays on), and it stops the p0f fingerprinting service, which needs a network capability Railway does not grant and otherwise restarts every second and floods the logs. It also flattens the upstream image into one layer, because Railway's runtime ignores the file deletions in the upstream layers: deleted Pro-only files reappear, the admin panel fails to compile and the database schema is never created.
Mail protocols are reachable on Railway's private network only: SMTP on 25 and submission on 587, IMAP on 143. Railway cannot give one service both an HTTP healthcheck and a public TCP proxy, so this template serves webmail, the admin panel and the API publicly and keeps mail ports for services in the same project. Outbound connections to port 25 on the internet timed out in a test from this project, so treat this deployment as the mail server for your own apps and agents inside the project, not as an internet-facing mail host.
Agent contract
- Public URL:
POSTE_PUBLIC_URL(https://plus the service'sRAILWAY_PUBLIC_DOMAIN). Webmail at/webmail/, admin panel at/admin/, REST API under/admin/api/v1. - Private URLs: web, admin and API
POSTE_PRIVATE_URL(http://poste.railway.internal:8080); mail hostPOSTE_MAIL_HOST(poste.railway.internal) with SMTP on 25, submission on 587 (login required, STARTTLS) and IMAP on 143 (login required, STARTTLS). - Credentials:
POSTE_ADMIN_EMAILandPOSTE_ADMIN_PASSWORDfor the admin panel and the API. Each mailbox you create has its own address and password. - Auth: the API takes HTTP Basic auth (
curl -u "$POSTE_ADMIN_EMAIL:$POSTE_ADMIN_PASSWORD"). SMTP submission and IMAP use the mailbox address and password; the server certificate is self-signed, so clients connect with--ssl-reqd -kor skip verification. - Health:
GET /webmail/returns 200 without credentials.GET /admin/api/v1/domainsreturns 401 without credentials. - API:
GET /admin/api/v1/domains,GETandPOST /admin/api/v1/boxes(JSONname,email,passwordPlaintext,superAdmin),DELETE /admin/api/v1/boxes/{email}.
# run from any service in the project that has curl, with these variables set:
# POSTE_PRIVATE_URL POSTE_MAIL_HOST POSTE_HOSTNAME POSTE_ADMIN_EMAIL POSTE_ADMIN_PASSWORD
U="$POSTE_PRIVATE_URL"; A="$POSTE_ADMIN_EMAIL:$POSTE_ADMIN_PASSWORD"; D="$POSTE_HOSTNAME"
BOX="agent@$D"; BOXPW=$(head -c 48 /dev/urandom | base64 | tr -dc 'A-Za-z0-9' | head -c 24)
curl -s -o /dev/null -w "webmail: %{http_code}\n" "$U/webmail/"
curl -s -o /dev/null -w "no auth: %{http_code}\n" "$U/admin/api/v1/domains"
curl -s -u "$A" -o /dev/null -w "domains: %{http_code}\n" "$U/admin/api/v1/domains"
curl -s -u "$A" -o /dev/null -w "create mailbox: %{http_code}\n" -X POST -H 'Content-Type: application/json' \
-d "{\"name\":\"agent\",\"email\":\"$BOX\",\"passwordPlaintext\":\"$BOXPW\",\"superAdmin\":false}" "$U/admin/api/v1/boxes"
printf 'From: %s\r\nTo: %s\r\nSubject: hello from an agent\r\n\r\nYour code is 482913\r\n' "$BOX" "$BOX" |
curl -s --ssl-reqd -k --url "smtp://$POSTE_MAIL_HOST:587" --user "$BOX:$BOXPW" --mail-from "$BOX" --mail-rcpt "$BOX" -T -; echo "smtp exit: $?"
for i in 1 2 3 4 5 6 7 8 9 10; do
MSG=$(curl -s --ssl-reqd -k --user "$BOX:$BOXPW" "imap://$POSTE_MAIL_HOST/INBOX;UID=1"); if [ -n "$MSG" ]; then break; fi; sleep 2
done
printf '%s\n' "$MSG" | grep -E '^(Subject|Your code)'
curl -s -u "$A" -o /dev/null -w "delete mailbox: %{http_code}\n" -X DELETE "$U/admin/api/v1/boxes/$BOX"
A fresh deploy prints webmail: 200, no auth: 401, domains: 200, create mailbox: 201, smtp exit: 0, the lines Subject: hello from an agent and Your code is 482913 read back over IMAP, and delete mailbox: 204.
To use it from your own service, point its SMTP settings at host POSTE_MAIL_HOST, port 587, STARTTLS with certificate checks off, the mailbox address as the user name and the mailbox password, then read mail back over IMAP on port 143.
Why Deploy Poste.io on Railway?
Railway is a singular platform to deploy your infrastructure stack. Railway will host your infrastructure so you don't have to deal with configuration, while allowing you to vertically and horizontally scale it.
By deploying Poste.io on Railway, you are one step closer to supporting a complete full-stack application with minimal burden. Host your servers, databases, AI agents, and more on Railway.
- Agent-ready - a generated admin login, a contract block and one working script; nothing to type into the deploy form and no wizard to click through.
- Mailboxes by API - an agent can create, use and delete a mailbox with plain HTTP and standard mail protocols.
- State survives redeploys - mail, mailboxes and the admin account live on a volume, and the image is pinned by digest.
- Works on Railway - upstream's own image deploys but never creates its database there; this wrapper fixes that and stops the log flood.
- Lean by default - ClamAV is off, so the server runs without its largest memory consumer while Rspamd keeps filtering spam.
Common Use Cases
- Mail for your apps - a staging or production app sends sign-up, receipt and password-reset mail through submission on
POSTE_MAIL_HOSTand reads replies over IMAP. - Agent mailboxes - an AI agent creates a mailbox per task, receives a verification code or a reply, and deletes the mailbox afterwards.
- Webmail for a team inside a project - run webmail and the admin panel on the public URL for a small group that needs inboxes.
Dependencies for Poste.io Hosting
- One Railway volume on
/data, created by the template.
Deployment Dependencies
Implementation Details
| Variable | Purpose |
|---|---|
POSTE_ADMIN_PASSWORD | Password of the admin user for the admin panel and REST API. Generated per deploy. |
POSTE_ADMIN_EMAIL | Admin login: admin@ plus the public domain. |
POSTE_HOSTNAME | Mail host name and the domain mailboxes are created under. |
POSTE_PUBLIC_URL | HTTPS address of webmail, the admin panel and the API. |
POSTE_PRIVATE_URL | The same web address on the private network. |
POSTE_MAIL_HOST | Private-network host name of the SMTP, submission and IMAP listeners. |
The wrapper entrypoint sets HTTPS=OFF and HTTP_PORT to the injected PORT, then hands over to the upstream init. An s6 service submits the install wizard once from these variables; it counts as finished when /data/server.ini exists, so a redeploy skips it. Poste.io is a product of Analogic; this listing wraps their image and is not affiliated with them.
Template Content
