# Mailstead documentation Self-hosted custom-domain webmail. Resend transports mail; your deployment stores mailboxes. ## Set up custom-domain email with Mailstead Source: https://mailstead.app/docs/agent-setup/ Mailstead is MIT-licensed self-hosted webmail. Deploy the application on Railway, store mailbox data on a persistent volume, and use Resend for inbound and outbound transport. The marketing site at mailstead.app does not host customer mailboxes. ## 1. Check the fit and gather inputs Use Mailstead when the user wants a browser inbox for custom-domain addresses and can operate one web application. It has no default IMAP or POP3 service, office suite, or managed mailbox hosting. If the task is only application notifications, a sending API may be sufficient without Mailstead. Gather the desired mailbox addresses, a Railway project, a Resend account/API key with the access needed for sending, receiving, and webhook management, DNS access, and an existing external recovery inbox. Use secure service variables for secrets. Never echo API keys, bootstrap tokens, passwords, or encryption keys into chat, logs, commits, or URLs in a handoff report. Clarify two different domains: | Input | Example | Purpose | | --- | --- | --- | | `MAIL_DOMAIN` | `example.com` | Addresses such as `hello@example.com`; verified for sending and receiving in Resend | | `PUBLIC_URL` | `https://inbox.example.com` or a Railway hostname | Browser access and the Resend webhook origin | A Railway hostname is enough for the web app. Do not change the website’s apex DNS merely to configure mail. ## 2. Inspect existing mail routing Inspect existing MX, SPF, DKIM, and DMARC records before writing DNS. An existing Google Workspace, Microsoft 365, forwarding, or other receiving setup needs an explicit migration decision. Do not mix unrelated MX targets to try to deliver the same address to both providers. Use a separate mail subdomain for a pilot when keeping the existing inbox service. Record the current DNS values for a possible rollback. Read the current provider-generated values; do not invent DNS targets, copy another domain’s DKIM, or create a second SPF record at the same name. See the [Cloudflare DNS guide](/guides/cloudflare-custom-domain-email/). ## 3. Deploy the application Use the [Railway template](https://railway.com/deploy/mailstead), which creates one Docker service with a volume mounted at `/data` and generated application secrets. Check the actual template result rather than assuming each step succeeded. | Variable | Required setup | | --- | --- | | `RESEND_API_KEY` | Store a real Resend API key in service variables | | `MAIL_DOMAIN` | Set the selected mail domain, with no scheme or address prefix | | `ADMIN_RECOVERY_EMAIL` | Use an existing inbox the admin can access | | `PUBLIC_URL` | Set the public HTTPS origin, without a path; the template defaults to Railway’s public hostname | | `DATA_ROOT` | `/data`, matching the persistent volume mount | | `SECRET_KEY`, `APP_ENCRYPTION_KEY`, `BOOTSTRAP_TOKEN` | Keep the template-generated values private and preserve them across redeploys | Optional `DATABASE_URL` changes the database to Postgres; mail blobs still need persistent storage. Optional VAPID keys enable Web Push. Neither is required for an initial inbox. For manual deployment, use the repository Dockerfile, mount `/data`, start `python -m mailstead.run`, and set `RAILWAY_DEPLOYMENT_DRAINING_SECONDS=30`. See [the repository deployment guide](https://github.com/mabounassif/mailstead/blob/main/docs/deploy-railway.md). ## 4. Verify DNS in Resend Add the chosen `MAIL_DOMAIN` to Resend. Follow its current instructions for both sending and receiving, including the receiving MX records. Sending-domain verification alone does not prove that inbound mail is enabled. Wait until Resend reports the required configuration ready. If using a custom webmail hostname, point that separate hostname at the target Railway provides. Confirm HTTPS before changing `PUBLIC_URL`. Keep mail routing records separate from web app routing. ## 5. Bootstrap and create mailboxes Open `/setup/` on the deployed application, using the token from private service variables. Create the first admin. Bootstrap is a one-time operation; do not reset data or rotate secrets to regain the setup screen. Create the required users and mailboxes in `/admin/`. Confirm exact addresses match `MAIL_DOMAIN`. Bootstrap also creates an admin mailbox when `MAIL_DOMAIN` is set; inspect it before adding duplicates. A domain or webhook does not automatically create every possible recipient mailbox. ## 6. Confirm inbound webhook registration The application registers a Resend webhook using `PUBLIC_URL`. Confirm a webhook exists at `https://YOUR_WEBMAIL_HOST/hooks/resend` and includes `email.received`. The endpoint is `/hooks/resend`, with no trailing slash. Check the app’s operations dashboard at `/ops/` and registration logs if it is missing or disabled. If the public hostname changes, update `PUBLIC_URL` and verify registration for the new endpoint. A stale webhook can leave the app healthy while inbound mail fails. ## 7. Check readiness and real delivery For a shell session, set `MAILSTEAD_URL` to the public app origin, without a trailing slash, then run: ```bash curl --fail --silent --show-error "$MAILSTEAD_URL/healthz" curl --fail --silent --show-error "$MAILSTEAD_URL/readyz" ``` `/healthz` is liveness. `/readyz` checks the database, volume writability, migrations, and worker heartbeat. It does **not** verify DNS, provider quotas, webhook delivery, or actual inbound/outbound mail. Complete the delivery checks with the user’s authorized external test inbox: - Sign in and confirm the intended mailbox exists. - Send a uniquely identifiable message from the external inbox to that mailbox. - Confirm the message appears inside Mailstead, not merely in Resend’s event log. - Reply from Mailstead and confirm receipt in the external inbox. - Inspect `/ops/` and provider usage for errors or quota limits. Do not claim these tests passed if you only verified the container. Report unavailable account access or a missing external test as pending. ## Troubleshooting | Symptom | Check | | --- | --- | | Healthy app, no inbound mail | Receiving DNS, exact mailbox address, `email.received` webhook, signing configuration, worker, and quota | | Webhook missing | Valid API key and permissions, public HTTPS `PUBLIC_URL`, registration logs | | Reply fails | Verified sending domain, API key access, quota, outbound job/error logs | | Readiness degraded | Database access, pending migrations, writable `/data`, worker heartbeat | | Custom hostname gives Bad Request | `PUBLIC_URL`, Railway custom domain, and the app’s allowed-host configuration | | Setup link no longer works | Bootstrap may already be consumed; use the existing admin account/recovery path | ## Handoff and day-two operations Report the public app URL, mailbox addresses, which checks passed, and remaining actions. Exclude secrets and the bootstrap URL. Explain that total cost includes hosting, Resend usage, the domain, backups, and maintenance; free software does not establish a zero total bill. Preserve the data volume and encryption key. Back up the volume as well as the metadata export: `python manage.py export` does not include mail blobs. See [operations](https://github.com/mabounassif/mailstead/blob/main/docs/operations.md). ## Sources - [Repository deployment guide](https://github.com/mabounassif/mailstead/blob/main/docs/deploy-railway.md). - [Readiness implementation](https://github.com/mabounassif/mailstead/blob/main/core/health.py). - [Webhook registration implementation](https://github.com/mabounassif/mailstead/blob/main/provider/webhook_register.py). - [Bootstrap implementation](https://github.com/mabounassif/mailstead/blob/main/core/bootstrap.py). ## Connect Cloudflare DNS to Mailstead and Resend Source: https://mailstead.app/guides/cloudflare-custom-domain-email/ To use `hello@example.com` in Mailstead, configure email delivery for `example.com` in Resend. To open webmail at `inbox.example.com`, configure that separate hostname in Railway. They are different DNS tasks. ## Inspect existing mail routing first Inspect the current MX records. If the domain receives mail through another provider, plan a migration before replacing its records. Keep the current values for rollback. A pilot on a separate subdomain can preserve the existing route, but its addresses will use that subdomain. Do not mix unrelated MX targets to try to duplicate delivery. If Cloudflare forwarding or another provider currently receives the same domain’s mail, review that configuration first. The receiving domain needs the records specified by your selected receiving provider. ## Configure the mail domain in Resend Add the selected domain to Resend and configure both sending and receiving. Copy the exact record names, types, priorities, and values shown by Resend into Cloudflare DNS. | Record | Purpose | Value to publish | | --- | --- | --- | | Receiving MX | Route incoming mail | Current Resend receiving values and priorities | | DKIM | Verify signed outgoing mail | Domain-specific values shown by Resend | | SPF / sending records | Authorize transport | Resend’s instructions reconciled with existing senders | | DMARC | Authentication policy and reporting | A policy appropriate to the actual domain senders | Do not create two SPF records at the same name, guess a DKIM target, or replace an existing DMARC policy without reviewing its effect. Follow the provider’s DNS-only requirements for mail-related hostnames where Cloudflare offers proxying. Wait for verification. A verified sending domain does not prove that inbound receiving is configured. ## Configure the webmail hostname in Railway Railway’s generated hostname works for the application while you test. For `inbox.example.com`: 1. Add that hostname to the Mailstead service’s custom domains in Railway. 2. Copy the exact DNS records Railway provides (including the CNAME target and any TXT verification record) into Cloudflare for the `inbox` hostname. Start DNS-only while verifying the hostname and TLS. 3. Wait for Railway to confirm the domain and HTTPS. 4. Set `PUBLIC_URL=https://inbox.example.com`. Keep `MAIL_DOMAIN=example.com` if that is your mail domain. 5. Confirm the Resend webhook points to `https://inbox.example.com/hooks/resend`. Keep the existing website records intact. The website can stay on `example.com` while webmail runs on `inbox.example.com`. ## Verify delivery Create the exact recipient mailbox. Check `/readyz`, receive a message from an outside account, and reply to it. Confirm external receipt. If receiving fails, inspect MX and the webhook before changing unrelated website records. Follow the [complete setup checklist](/docs/agent-setup/) or [give the guide to your coding agent](/for-agents/). ## Sources - [Mailstead deployment](https://github.com/mabounassif/mailstead/blob/main/docs/deploy-railway.md). - [Resend custom receiving domains](https://resend.com/docs/receiving/custom-domains). - [Cloudflare Email Routing DNS](https://developers.cloudflare.com/dns/troubleshooting/email-issues/). - [Railway public networking](https://docs.railway.com/networking/domains/working-with-domains). ## Give your project a customer email inbox Source: https://mailstead.app/guides/customer-email-inbox/ A project website can send notifications and still have nowhere useful to read customer replies. Mailstead supplies that browser inbox: incoming mail, sent messages, drafts, attachments, and search, using Resend for transport. A founder can create `hello@example.com`, receive a customer’s question, and reply from that address. This is a mailbox workflow. Mailstead does not claim shared ticket assignment, help-desk SLAs, or an office suite. ## Choose the address and webmail hostname Pick the mailbox address first. `MAIL_DOMAIN=example.com` uses addresses on `example.com`; `MAIL_DOMAIN=mail.example.com` uses the subdomain. Then choose where to open webmail: a Railway hostname or a custom host such as `inbox.example.com`. If the mail domain already has a receiving provider, review the [DNS and routing guide](/guides/cloudflare-custom-domain-email/) before changing it. ## Deploy beside your application Deploy Mailstead as its own service using the [Railway template](https://railway.com/deploy/mailstead). Keep the persistent volume at `/data`. Supply the Resend API key, mail domain, and an external recovery email as secure variables. Verify sending and receiving in Resend, bootstrap the first administrator, and create the exact recipient mailbox. Confirm inbound events reach the webhook at the app origin plus `/hooks/resend`. The [full runbook](/docs/agent-setup/) gives the variables, deployment sequence, troubleshooting, and delivery checks. If a coding agent is doing the setup, [give it the guide](/for-agents/). ## Test the conversation Send from an external account to the new address. Check that it appears in Mailstead, then reply and verify external receipt. A successful application notification does not establish that customer replies reach this inbox. Once verified, publish the address on your website. For application-generated email, use the appropriate reply-to address when you want responses to reach this mailbox. Keep sender authentication and provider limits in mind. ## Budget for the whole inbox Software is MIT licensed. Running cost includes hosting, transport, the domain, storage/backups, and administration. An existing Railway subscription can reduce incremental spending; it does not establish a universally free deployment. Use the [pricing calculator](/pricing/) with expected received and sent volume. Add the users and mailboxes you need and review quotas. The project targets small teams; it has no published capacity benchmark for a specific headcount. ## Sources - [Repository features](https://github.com/mabounassif/mailstead/blob/main/README.md). - [Deployment guide](https://github.com/mabounassif/mailstead/blob/main/docs/deploy-railway.md). - [Operations and backups](https://github.com/mabounassif/mailstead/blob/main/docs/operations.md). ## What does custom-domain email actually cost? Source: https://mailstead.app/guides/custom-domain-email-cost/ Custom-domain email is the address `you@yourdomain`. The invoice is rarely one line. You pay for a domain, for someone to accept and deliver mail, for a place to read it, and for the time to keep that arrangement working. This guide is for people comparing a hosted suite with running their own webmail. Amounts are USD list prices checked on 14 October 2026. They are not a quote for your renewal. ## The pieces that show up on a bill | Piece | What it pays for | How it usually scales | | --- | --- | --- | | Domain | The name in the address | A yearly registrar fee, whether you use Google or a server you run | | Seats or mailboxes | The right to have a person or a shared address | Hosted suites often bill a licence per user. A shared address is not automatically another seat; read that vendor’s policy | | Transport | Sending and receiving on the public internet | Included in most suites. With Mailstead it is a separate Resend plan, and received mail counts toward the quota | | Storage | Mailbox contents and file retention | Pooled gigabytes per user at Google, per-user caps at Zoho, or your own disk for a self-hosted app | | Administration | DNS, accounts, backups, failures | A suite hides most of it. Self-hosting puts it on your calendar | Mailstead’s own software is $0 under the MIT licence. That line does not include Railway, Resend, or the domain. The [pricing page](/pricing/) separates a new hosting bill from the extra cost on a hosting account you already pay for. ## Hosted mail, priced as licences Google Workspace Business Starter is $7 per user per month on the annual plan ($84 per user per year) or $8.40 per user per month with no annual commitment. Both figures are Google’s published US list prices. Storage on Starter is 30 GB pooled per user. Business plans stop at 300 users. The public pricing page has also shown an introductory discount for new customers; this guide uses the standard list, because an introduction is not the renewal price. Source: [Flexible and annual plans](https://knowledge.workspace.google.com/admin/billing/compare-flexible-and-annual-fixed-term-payment-plans), checked 2026-10-14. Microsoft’s US English page lists Exchange Online (Plan 1) at $4 per user per month, paid yearly. That is hosted mail, not the Office bundle. Microsoft 365 Business Basic is listed at $6 per user per month, paid yearly, and adds web apps, Teams, and file storage. A month-to-month Microsoft price was not on the pages checked, so it is not stated here. Sources: [Exchange Online](https://www.microsoft.com/en-us/microsoft-365/exchange/exchange-online) and [Business Basic](https://www.microsoft.com/en-us/microsoft-365/business/microsoft-365-business-basic), checked 2026-10-14. Zoho’s pricing JSON, which the mail pricing page loads, lists Mail Lite (5 GB) at $1 per user per month and marks monthly billing as yearly-only. Mail Premium is $4 per user per month, also yearly-only, with 50 GB of mail and 50 GB of retention on the comparison tables. The free plan is $0 for up to five users on one domain where Zoho offers it, with 5 GB per user, and the page says IMAP, POP, and ActiveSync are not included. Sources: [Zoho Mail pricing](https://www.zoho.com/mail/zohomail-pricing.html) and the [pricing JSON](https://www.zohowebstatic.com/sites/zweb/json/pricing/mail-pricing-val.json) it requests, checked 2026-10-14. Ten Business Starter users on the annual list is $70 per month before tax. Ten Exchange Online Plan 1 users on the yearly list is $40. Ten Zoho Mail Lite users is $10, prepaid as a year. None of those totals include your domain. ## Self-hosting the inbox, not the mail server Mailstead is one Docker service. It stores mail. [Resend](https://resend.com/pricing) sends and receives it. Resend Free is $0 for 3,000 emails a month and 100 a day. Crossing either limit means a paid plan; Pro starts at $20 per month for 50,000 emails. A team of 50 can miss the free tier on volume alone. [Railway Hobby](https://railway.com/pricing) is $5 per month and includes $5 of resource usage. If measured usage stays inside that credit, the hosting line stays $5. Usage above the credit is billed at Railway’s resource rates. That is a new account. Someone who already pays for Hobby and still has unused credit may add no hosting dollars for a small service. The [calculator](/pricing/#calculator) asks for that unused credit so the two cases stay separate. ## Worked month, labelled as an illustration Assume 10 mailboxes and 20 messages each, sent and received together: 200 messages. The busiest day, if traffic is even across 30 days, is 7 messages. Resend Free covers that. A new Railway Hobby account with $3 of usage still costs $5, because the subscription is the minimum. Optional backups are extra and were left at $0. Domain renewal is your registrar’s number, divided by 12 if you want a monthly view. The same 10 people on Workspace Business Starter’s annual list is $70 before the domain. The products are not equivalent: Starter includes Gmail’s suite, and Mailstead does not. Change the illustration to 10,000 messages in a month and Resend Free no longer fits. The model then uses Pro at $20. Headcount did not cause that. Volume did. ## Backups and time Mailstead’s operations notes recommend volume backups plus `python manage.py export`. The export command writes portal metadata, not the mail blobs. Budget a backup you can restore, or accept the risk. Put administration next to the invoice as hours times a rate. A suite still needs an admin for users and DNS; a self-hosted app also needs deploys and a look at disk and provider quotas. ## When a subscription is the better purchase Buy a suite if you need calendars, office files, a vendor spam filter with a published efficacy claim, desktop mail clients, or a contract and support desk. Buy hosted mail if you will not operate an application. Use the comparisons for [Workspace](/compare/mailstead-vs-google-workspace/), [Microsoft 365](/compare/mailstead-vs-microsoft-365/), [Zoho](/compare/mailstead-vs-zoho-mail/), and [mailcow](/compare/mailstead-vs-mailcow/) when you already know which alternative you are weighing. ## Sources - Google, [compare Flexible and annual plans](https://knowledge.workspace.google.com/admin/billing/compare-flexible-and-annual-fixed-term-payment-plans), checked 2026-10-14. - Microsoft, [Exchange Online (Plan 1)](https://www.microsoft.com/en-us/microsoft-365/exchange/exchange-online) and [Business Basic](https://www.microsoft.com/en-us/microsoft-365/business/microsoft-365-business-basic), checked 2026-10-14. - Zoho, [Mail pricing](https://www.zoho.com/mail/zohomail-pricing.html) and [mail-pricing-val.json](https://www.zohowebstatic.com/sites/zweb/json/pricing/mail-pricing-val.json), checked 2026-10-14. - Resend, [pricing](https://resend.com/pricing), checked 2026-10-14. Received emails count toward the transactional quota. - Railway, [pricing](https://railway.com/pricing), checked 2026-10-14. - Mailstead [LICENSE](https://github.com/mabounassif/mailstead/blob/main/LICENSE) and [operations](https://github.com/mabounassif/mailstead/blob/main/docs/operations.md). ## Mailstead vs Google Workspace: email costs and trade-offs Source: https://mailstead.app/compare/mailstead-vs-google-workspace/ Google Workspace Business Starter is a bundle: Gmail on your domain, storage, meetings, and the rest of the starter suite. Mailstead is a webmail application you run, with Resend carrying mail in and out. If you need the bundle, Starter is the product. If you need mailboxes and already operate a small app, the bundle is a different purchase. Prices below are Google’s US dollar list, checked on 14 October 2026. The [general cost guide](/guides/custom-domain-email-cost/) explains domains and admin time. This page stays on Workspace. ## List price, not the introduction | Plan | Commitment | Price | Storage | | --- | --- | --- | --- | | Business Starter, Flexible | None. Billed monthly for the accounts you have | $8.40 per user per month | 30 GB pooled per user | | Business Starter, annual | One year for the licences you buy at the start | $7 per user per month, or $84 per user per year | 30 GB pooled per user | Source: [Compare Flexible and annual plans](https://knowledge.workspace.google.com/admin/billing/compare-flexible-and-annual-fixed-term-payment-plans) and [business editions](https://knowledge.workspace.google.com/admin/getting-started/editions/business-editions), checked 2026-10-14. Business Starter, Standard, and Plus stop at 300 users. [workspace.google.com/pricing](https://workspace.google.com/pricing) also showed an introductory price for new customers, limited to the first 20 users and 12 months, with standard pricing afterward. Do not put that introduction in a renewal forecast. Canadian list prices on the same help page are C$11.00 (Flexible) and C$9.20 (annual) per user per month. They are Google’s CAD prices, not a conversion, and they are not subtracted from the USD totals elsewhere on this site. ## What the money buys | | Mailstead | Business Starter | | --- | --- | --- | | Inbox | Browser app you host: folders, compose, reply, forward, attachments, search | Gmail | | Licence | $0 software. Hosting and Resend are separate | Per user, as in the table | | Docs, Drive, Meet | Not included | Included in the suite | | Desktop mail protocol | Not provided | Not compared here; this page does not restate client setup | | Spam handling | Local rules and DMARC-fail routing to Junk. No efficacy percentage | Google markets phishing and spam protection on the pricing page. This site does not repeat an unverified block rate | | Who operates it | You deploy, back up, and watch quotas | Google operates Gmail | An illustrative Mailstead month is on the [pricing page](/pricing/#scenario): 20 messages per mailbox, Hobby hosting, Resend chosen from that volume. For 10 mailboxes that illustration stays on Resend Free and a $5 Hobby minimum if usage is inside the included credit. Ten Starter users on the annual list are $70 per month before tax. The $65 gap is not “savings,” because Starter includes the suite and a vendor-operated mailbox, and the illustration collapses as soon as message volume leaves Resend Free. ## Who should pick which Choose Workspace if several people expect Drive, Docs, Meet, or an admin console Google already runs. Choose Mailstead if the requirement is custom-domain webmail, the team can run one container, and the missing suite is acceptable. A company that wants both mail and the office apps should buy Workspace and ignore the mailbox-only arithmetic. Mailstead does not include end-to-end encryption, a compliance vault, or a promise that the 10–50 person design target has been load-tested. ## Sources - Google, [annual and Flexible prices](https://knowledge.workspace.google.com/admin/billing/compare-flexible-and-annual-fixed-term-payment-plans), checked 2026-10-14. - Google, [business editions](https://knowledge.workspace.google.com/admin/getting-started/editions/business-editions), checked 2026-10-14. - Google, [pricing page](https://workspace.google.com/pricing), introductory offer noted 2026-10-14. - Resend and Railway figures used only by reference from the [pricing model](/pricing/), same check date. ## Mailstead vs Microsoft 365: when self-hosted webmail makes sense Source: https://mailstead.app/compare/mailstead-vs-microsoft-365/ “Microsoft 365” on a quote can mean hosted Exchange only, or a bundle of apps, Teams, and security. Those are different prices. This page uses two US English list prices checked on 14 October 2026, and it does not average them. Mailstead is the thing you compare when the requirement is a web inbox on your domain and you will operate the app. It is a poor comparison when the requirement is Outlook on the desktop, Word, or device management. ## Two Microsoft products | Product | What the page says you are buying | Price shown | Commitment on that page | | --- | --- | --- | --- | | Exchange Online (Plan 1) | Hosted email | $4.00 per user per month | Paid yearly. Annual subscription, auto-renews | | Microsoft 365 Business Basic | Web and mobile apps, professional email, cloud file storage, online meetings, and AI chat | $6.00 per user per month | Paid yearly | | Business Premium with Copilot | Apps, device security, and Copilot | $32.00 per user per month | Paid yearly, annual subscription, auto-renews | Sources, checked 2026-10-14: [Exchange Online](https://www.microsoft.com/en-us/microsoft-365/exchange/exchange-online), [Business Basic](https://www.microsoft.com/en-us/microsoft-365/business/microsoft-365-business-basic), and [business plans](https://www.microsoft.com/en-us/microsoft-365/business/microsoft-365-plans-and-pricing). Microsoft’s licensing FAQ says business plans can be monthly or annual, and that annual commitment is discounted. The month-to-month dollar amount was not on the pages captured, so this article does not invent it. Business plans are described as supporting up to 300 users. Exchange storage quotas and Outlook connectivity details were not re-copied from a service description in this pass. Where that page was not opened, the cell stays blank rather than borrowed from memory. ## Against Mailstead | Question | Mailstead | Exchange Online Plan 1 | Business Basic | | --- | --- | --- | --- | | Who runs the mailbox service? | You run the app. Resend transports mail | Microsoft | Microsoft | | Software licence | $0, MIT | Included in the user price | Included in the user price | | Extra apps | None | This page treats it as email hosting | Web apps, Teams, files, meetings | | IMAP into Mailstead | No | Not claimed here | Not claimed here | | 25 users, yearly list, before tax | Depends on hosting and message volume. See the [scenario table](/pricing/#scenario) | $100 per month at $4 | $150 per month at $6 | Twenty-five Business Premium with Copilot seats at the yearly list would be $800 per month. That number is a different product. Do not use it as the cost of “email.” A quiet Mailstead deployment can sit on Railway Hobby at $5 per month plus Resend Free if sent and received mail stay within 3,000 per month and 100 per day ([Resend pricing](https://resend.com/pricing)). Fifty people at a few dozen messages a day will not. The [calculator](/pricing/#calculator) is the place to change volume. It will not tell you that Mailstead includes Teams. ## When Microsoft is the right bill Pick Exchange Online Plan 1 when you want Microsoft to host mail and you do not want the app bundle, and you can live with a yearly commitment at the price above. Pick Business Basic when the team wants the web apps and meetings that come with it. Pick a Premium or Copilot plan only after you have priced that plan, not Plan 1. Pick Mailstead when a browser inbox is enough, IMAP is not required, and someone will deploy the container, verify DNS, and keep backups. The operations guide’s export is metadata, not a full mailbox archive. ## Sources - Microsoft, [Exchange Online (Plan 1)](https://www.microsoft.com/en-us/microsoft-365/exchange/exchange-online), $4 user/month paid yearly, checked 2026-10-14. - Microsoft, [Business Basic](https://www.microsoft.com/en-us/microsoft-365/business/microsoft-365-business-basic), $6 user/month paid yearly, checked 2026-10-14. - Microsoft, [plans and pricing](https://www.microsoft.com/en-us/microsoft-365/business/microsoft-365-plans-and-pricing), Business Premium with Copilot at $32, checked 2026-10-14. - Microsoft, [licensing FAQ](https://www.microsoft.com/licensing/faqs/5), monthly versus annual commitment described without a month-to-month Business Basic price, checked 2026-10-14. ## Mailstead vs Zoho Mail: budget custom-domain email compared Source: https://mailstead.app/compare/mailstead-vs-zoho-mail/ Zoho Mail is hosted email with a free tier and low annual mail plans. Mailstead is free software that still needs a host and a transport provider. For a team that wants someone else to store the mail and is willing to prepay a year, Zoho is the direct budget comparison. For a team that wants the mailbox bytes on its own volume, Mailstead is. USD figures come from the pricing JSON that [Zoho’s mail pricing page](https://www.zoho.com/mail/zohomail-pricing.html) requests. The visible page left price cells empty until that file loaded. Currency order in the file starts with USD. Checked 14 October 2026. ## Plans that are actually about mail | Plan | USD | Billing | Storage and access, from the pricing page | | --- | --- | --- | --- | | Mail Free | $0 | — | Up to 5 users on one domain where the free plan is offered. 5 GB mail per user. IMAP, POP, and ActiveSync are not included. From the sixth user, Zoho’s tooltip says users enter a paid plan | | Mail Lite, 5 GB | $1 per user per month | Yearly only. The monthly price cell says “Yearly subscriptions only” | 5 GB mail per user on the plan toggle | | Mail Lite, 10 GB | $1.25 per user per month | Yearly only | 10 GB mail per user | | Mail Premium | $4 per user per month | Yearly only | 50 GB mail per user and 50 GB retention per user, on the comparison content | | Workplace Standard | $4 monthly, or $3 per user per month billed annually | Both | 30 GB mail per user. This is a workplace bundle, not mail-only | | Workplace Professional | $7 monthly, or $6 per user per month billed annually | Both | 100 GB mail and 100 GB retention per user. Also a bundle | The JSON set ids are `zplan-mail-lite`, `zplan-mail-10gb`, `zplan-premium`, `zplan-basic-monthly`, `zplan-basic-yearly`, `zplan-ultimate-monthly`, and `zplan-ultimate-yearly`. File: [mail-pricing-val.json](https://www.zohowebstatic.com/sites/zweb/json/pricing/mail-pricing-val.json). CAD in that same file, not a conversion: Mail Lite 5 GB is 1.25, Mail Lite 10 GB is 1.57, and Mail Premium is 5, in the CAD column. Zoho’s FAQ says an annual billing discount is about 20 percent and can vary by region. For Mail Lite the file does not offer a monthly price at all. Zoho’s FAQ also says the five free users exist only on the free mail plan. Paid plans bill every user. Five Mail Lite users are $5 per month on the annual list, paid as a year, not “five free plus a discount.” ## Where Mailstead is a different shape | | Zoho Mail Lite | Mailstead | | --- | --- | --- | | Who stores messages | Zoho | Your volume, in the app | | Per-user mail price | $1 per month, 5 GB, annual prepay | $0 software. Disk is whatever the volume costs | | Protocols | Paid plans are the ones that move past the free plan’s missing IMAP, POP, and ActiveSync. This page does not invent which protocols Lite enables beyond leaving the free-plan restriction | No IMAP, POP, or Exchange in the default app | | Transport quota | Zoho’s mail plans are seats and storage, not a 3,000-message Resend cap | Resend Free is 3,000 emails per month and 100 per day, received mail included | | Five users, low volume | $0 on the free plan if you accept its limits, or $5 per month on Lite if you need what free withholds | Hobby hosting at $5 per month minimum on a new Railway account, plus Resend at $0 only while volume fits | Workplace Standard at $3 per user per month on annual billing is the wrong row if you only wanted Mail Lite. It adds Zoho’s workplace apps. Keep it in a bundle conversation. ## When Zoho is the better fit Choose Zoho Mail when you want hosted mail, a published per-user storage cap, and you will pay the year upfront on Lite or Premium. Choose the free plan only if five users, one domain, 5 GB, and no IMAP/POP/ActiveSync match the real constraint, and only in a region where Zoho still offers that plan. The page says the free plan is for select regions. Choose Mailstead when the mailbox store should sit on your disk and you accept Resend as transport, a browser-only client, and your own backups. Neither option removes the domain renewal. ## Sources - Zoho, [zohomail-pricing.html](https://www.zoho.com/mail/zohomail-pricing.html), free-plan limits and FAQ, checked 2026-10-14. - Zoho static file, [mail-pricing-val.json](https://www.zohowebstatic.com/sites/zweb/json/pricing/mail-pricing-val.json), USD and CAD columns, checked 2026-10-14. - Resend quota rules are summarized from [resend.com/pricing](https://resend.com/pricing), same date, and spelled out in [Can you use Resend for a team inbox?](/guides/resend-webmail/). ## Mailstead vs mailcow: API-backed webmail or a full mail server? Source: https://mailstead.app/compare/mailstead-vs-mailcow/ mailcow and Mailstead both end in a browser inbox. They do not start from the same machine. mailcow: dockerized is a mail server and groupware: SMTP, IMAP, POP3, antivirus, and a database stack you operate. Mailstead is one web application. Resend accepts and delivers the mail; Mailstead stores copies and shows them. If Thunderbird, a phone’s mail app, or port 25 matters, start with mailcow or another real mail server. If those protocols are a burden you are trying to avoid, Mailstead exists for that case. ## What you run | | Mailstead default deploy | mailcow, from the project docs | | --- | --- | --- | | Processes | One container. Jobs run in that process. No Redis or Celery | Docker Compose groupware. Docs name nginx, Postfix, Dovecot, Rspamd, MariaDB, Redis, and more | | Minimum RAM | Not published as a benchmark. The app is a Django service plus a volume | 6 GiB RAM and 1 GiB swap. 8 GiB recommended for about 5 to 10 users. A site with 15 phones on ActiveSync and about 50 concurrent IMAP connections should plan 16 GiB | | Disk before mail | Volume you attach. Railway Hobby allows up to 5 GB | 20 GiB without emails | | Protocols | HTTPS for the app. No SMTP, IMAP, or POP3 listener | Ports 25, 465, 587, 143, 993, 110, 995, 4190, plus 80 and 443 | | External dependency | Resend API and webhooks, or mail does not flow | Your own MTA. Docs also list update downloads, spam-list connections, and outbound SMTP | | Licence cost | MIT, $0 | mailcow’s docs describe the software stack; this page does not restate a support contract | mailcow source: [Prepare your system](https://docs.mailcow.email/getstarted/prerequisite-system/), checked 2026-10-14. The page says OpenVZ, LXC, and NAS appliances are unsupported, and that a correct DNS setup is required before installation. ClamAV can be disabled if RAM is tight. Those are mailcow’s words about mailcow. Mailstead’s default path is the [Railway template](https://railway.com/deploy/mailstead): a volume at `/data`, health check at `/healthz`, bootstrap, then DNS at Resend. Railway Hobby is $5 per month including $5 of usage ([Railway pricing](https://railway.com/pricing)). That fee does not buy port 25, and many clouds block outbound port 25 anyway. Mailstead never needed it, because Resend sends the message. ## Where each design fails mailcow fails closed when you wanted “email” but received a mail system: firewall rules, IP reputation, certificate renewals, and spam updates. The docs are frank that this is groupware, not a tiny MTA. It is the better fit when IMAP or ActiveSync is a requirement, when mail must not depend on a third-party sending API, or when you already have a VM with the RAM above and an address that can speak SMTP. Mailstead fails closed when Resend rejects the domain, the daily or monthly quota is exhausted, or you need a desktop client. Local junk handling is a filter rule plus DMARC-fail routing. It is not ClamAV or Rspamd. There is no claim of a tested ceiling at 50 users. You can run Mailstead beside other Railway services and keep one volume. You should not expect to run mailcow inside a small application container. The mailcow docs tell you not to put it on container platforms that are not full virtualization. ## A practical split Use mailcow, or a hosted suite, when the mailbox has to speak IMAP. Use Mailstead when the mailbox has to be a website, the transport can be Resend, and the operator wants one service. The [deploy guide](/guides/deploy-mailstead-on-railway/) is the Mailstead procedure. It will not install Postfix. ## Sources - mailcow documentation, [prerequisite system](https://docs.mailcow.email/getstarted/prerequisite-system/), RAM, disk, ports, and unsupported platforms, checked 2026-10-14. - Mailstead [README](https://github.com/mabounassif/mailstead/blob/main/README.md) and [Railway deploy notes](https://github.com/mabounassif/mailstead/blob/main/docs/deploy-railway.md). - Railway, [pricing](https://railway.com/pricing), Hobby fee and included usage, checked 2026-10-14. ## How to deploy Mailstead on Railway Source: https://mailstead.app/guides/deploy-mailstead-on-railway/ Use the published template if you want the path this project documents: [Deploy Mailstead](https://railway.com/deploy/mailstead). It creates one service from the repository image, a volume mounted at `/data`, and generated secrets. It does not verify your domain, create mailboxes, or send a test message. Those remain after the container is up. You need a Resend API key with access to send and receive, a domain whose DNS you can edit, and an address for admin password recovery. Postgres and Web Push are optional. ## After the template finishes 1. Wait until the health check passes. Liveness is `/healthz`. Readiness is `/readyz`, which also looks at the worker heartbeat. 2. Open `https://YOUR_DOMAIN/setup/BOOTSTRAP_TOKEN` once. The token is a service variable. Create the first admin there. 3. In Admin (`/admin/`), create users and mailboxes on `MAIL_DOMAIN`. 4. In Resend, verify that domain and publish the DNS records Resend shows you. The app registers its inbound webhook when `PUBLIC_URL` is a public origin. The template sets `PUBLIC_URL` from Railway’s public domain. 5. Confirm the webhook is present, then send mail to a mailbox and reply from it. The start command in a manual service is `python -m mailstead.run`. It migrates, then runs one gunicorn worker. Jobs run in that same process. Set `RAILWAY_DEPLOYMENT_DRAINING_SECONDS=30` on a manual service. The template’s variable list is in [deploy-railway.md](https://github.com/mabounassif/mailstead/blob/main/docs/deploy-railway.md). ## What has to stay attached The volume at `/data` holds SQLite and mail blobs unless you set `DATABASE_URL` for Postgres. Removing the volume wipes that store. Hobby’s platform limits include a 5 GB maximum volume and two custom domains ([Railway pricing](https://railway.com/pricing), checked 2026-10-14). The Railway-provided `*.up.railway.app` hostname is enough for `PUBLIC_URL` while you test. Your mail domain is the Resend domain, which can be different from the hostname people use to open the app. Required variables: `RESEND_API_KEY`, `MAIL_DOMAIN`, `ADMIN_RECOVERY_EMAIL`, plus generated `SECRET_KEY`, `APP_ENCRYPTION_KEY`, and `BOOTSTRAP_TOKEN`. Web Push needs `VAPID_PUBLIC_KEY` and `VAPID_PRIVATE_KEY`. Without them, the app still runs; push stays off. ## Send and receive checklist - Health check is green. - You can sign in as the admin you created. - Resend shows the domain as verified. - A webhook points at `https://YOUR_PUBLIC_URL/hooks/resend`. - A message from an outside account arrives in the mailbox, not only in Resend’s logs. - A reply leaves through Resend and arrives at the outside account. - `/ops/` shows storage and the worker. The usage panel shows Resend quota, which needs an API key that can read usage. If inbound mail never appears, check `PUBLIC_URL`, the webhook signing setup, and that the recipient is a mailbox on `MAIL_DOMAIN`. If outbound mail fails, check the API key and the Resend quota. Received messages count toward that quota. See [Resend for a team inbox](/guides/resend-webmail/). ## Day two Redeploying runs migrations on start and does not reset the bootstrap token. Backups are a volume backup plus `python manage.py export` for configuration and metadata, not a blob export. Details: [operations](https://github.com/mabounassif/mailstead/blob/main/docs/operations.md). This marketing site is not the webmail service. Deploying Mailstead does not publish mailstead.app, and mailstead.app will not show your mail. ## Sources - Repository, [docs/deploy-railway.md](https://github.com/mabounassif/mailstead/blob/main/docs/deploy-railway.md) and [README](https://github.com/mabounassif/mailstead/blob/main/README.md). - Railway, [pricing and plan limits](https://railway.com/pricing), checked 2026-10-14. - Resend, [pricing](https://resend.com/pricing), inbound mail counts toward the transactional quota, checked 2026-10-14. ## Can you use Resend for a team inbox? Source: https://mailstead.app/guides/resend-webmail/ Resend can carry mail for your domain. It does not, by itself, become the shared inbox your team lives in. Mailstead is that inbox: folders, replies, and a database on your volume. Resend is the path messages take to and from the rest of the internet. You can use Resend for a team inbox only together with an application that stores mail. Mailstead is one such application. Resend’s own Inboxes product is a different feature, and Mailstead does not use it. ## Two products that both say “inbox” | | Resend transport, as Mailstead uses it | Resend Inboxes, on the pricing page | | --- | --- | --- | | API | `Emails.send`, `Emails.Receiving`, and webhooks | A hosted inbox you create in Resend | | Where the mailbox lives | In Mailstead, on your volume | In Resend. Every plan includes 3, then $1 per extra inbox per month on paid plans with pay-as-you-go | | Quota | Sent and received mail count toward the transactional quota | The pricing page says inbox emails also use the transactional quota | Checked against [Resend pricing](https://resend.com/pricing) on 14 October 2026, and against Mailstead’s client in `provider/resend_client.py`, which calls send, receiving get/list, webhooks, domains, and usage. There is no call that creates a Resend Inbox. The $1 inbox charge is not part of a Mailstead bill unless you turn that product on yourself. ## Quotas that decide whether a team fits | Plan | Monthly price | Included transactional emails | Daily cap | Overage | | --- | --- | --- | --- | --- | | Free | $0 | 3,000 | 100 emails a day | None | | Pro | $20 | 50,000 | No daily limit on the pricing page | $0.90 per 1,000 if pay-as-you-go is enabled | | Pro | $35 | 100,000 | No daily limit | $0.90 per 1,000 | Received email counts as one of those messages. A mailbox that gets 80 messages and sends 30 has used 110, not 30. Free allows one webhook endpoint; Mailstead registers one endpoint at `/hooks/resend`. Paid plans add endpoint room the app does not need for that single hook. Overage is optional and billed in buckets of 1,000. If you do not enable it, Resend’s FAQ says you are asked to upgrade instead of continuing. The [calculator](/pricing/#calculator) selects a plan that includes the month. It does not assume overage. Self-serve Resend plans are monthly. There is no annual discount. Data retention on Free, Pro, and Scale is 30 days on Resend’s side. That retention is Resend’s logs, not your Mailstead archive. Mailstead keeps what it stored, subject to your disk and your backups. ## What still breaks - The domain must be verified, or Resend will not send as you. - `PUBLIC_URL` must be reachable, or the webhook is never registered. - A full volume can leave Mailstead holding metadata while Resend still has the original. See the operations notes. - Resend rate limits are per team and, on the pricing FAQ, do not rise when you upgrade. Mailstead paces its own client because of that. - Junk mail is a local rule and a DMARC failure routed to Junk. Resend’s suppression list is theirs. Neither one is a promise about spam you will or will not see. A team of 50 with a busy shared address can leave the free quota in a few days. Count messages before you tell anyone the provider line is $0. The [deploy guide](/guides/deploy-mailstead-on-railway/) is the setup sequence once you have decided the quota fits. ## Sources - Resend, [pricing](https://resend.com/pricing), including “How are inbound emails counted?” and the Inboxes table, checked 2026-10-14. - Mailstead, `provider/resend_client.py` and `provider/webhook_register.py` in the [repository](https://github.com/mabounassif/mailstead). ## Can custom-domain email have no extra monthly cost? Source: https://mailstead.app/guides/custom-domain-email-no-extra-monthly-cost/ Sometimes the extra line on this month’s card is $0. The cost of having custom-domain email is not. Those are different questions. This page answers the first one with conditions you can check, and it refuses the second as a slogan. Nothing here is a measured story about a particular production mailbox. Usage, the Railway invoice, and the date of that usage were not recorded in the repository, so this article does not describe one person’s bill as a case study. ## Marginal cost and total cost Total cost is everything required: the domain renewal, the hosting plan you already pay or would have to start, Resend, backups, and time. Marginal cost is what changes because you added Mailstead. | You already have | Adding Mailstead changes the monthly card when | | --- | --- | | A domain | It doesn’t, until the year rolls over. Amortize the renewal if you want it in a monthly total. The [calculator](/pricing/#calculator) divides the annual price by 12 | | Railway Hobby at $5, with unused included usage | Hosting increment is $0 while this service’s measured usage fits in the unused credit. Hobby includes $5 of usage. The credit resets each cycle and does not roll over | | Nothing on Railway | You pay at least the Hobby fee, $5, even if resource usage is $1 | | Resend Free, and this month stays within 3,000 emails and 100 on the busiest day | Provider increment is $0. Received mail counts | | More mail than that | Provider increment is at least the Pro fee, $20 for 50,000 emails | Sources, checked 2026-10-14: [Railway pricing](https://railway.com/pricing), [Resend pricing](https://resend.com/pricing). ## A conditional example, in USD Assume the workspace already pays for Hobby, $4 of the $5 usage credit is still free, the domain renewal is already paid, backups are left at $0, and the month is 400 messages with no day above 100. - Standalone total, as if this were the only Railway project: $5.00 hosting plus $0 Resend. - Incremental total: $0 hosting plus $0 Resend. The $5 did not disappear. It was already on the invoice. Call the incremental total $0. Do not call email free. Change one assumption and the zero goes away: | Change | Incremental provider or host | | --- | --- | | 3,001 messages in the month | Resend Pro at $20. Hosting can still be $0 | | 101 messages on one day, even if the month is under 3,000 | Free is ineligible. The next included plan in this model is Pro at $20 | | Usage of $6 and only $4 of credit left | $2 of Railway usage, plus Resend | | A new Railway account | $5 Hobby minimum, even when usage is inside the credit | | A domain that costs $18 per year and was not already owned | $1.50 per month if you amortize it. Google, Zoho, and Microsoft need that domain too | Administration sits outside both totals. An hour spent on DNS is an hour, whether or not the card changed. ## What people mean by free, and what fails Zoho’s free mail plan is $0 for up to five users on one domain in regions where it is offered, with 5 GB per user and without IMAP, POP, or ActiveSync ([Zoho pricing](https://www.zoho.com/mail/zohomail-pricing.html)). That is a hosted free tier with product limits, not an unused Railway credit. Workspace and Exchange Online Plan 1 do not have a permanent $0 custom-domain tier on the pages checked for the [cost guide](/guides/custom-domain-email-cost/). Mailstead will not stay at a $0 provider line if the team’s mail grows. There is no load test that says 50 people fit in Resend Free. Count a month of sent plus received mail before you tell the team there is no extra charge. The software remains $0 either way. The MIT licence is not a hosting credit. ## Sources - Railway, [pricing](https://railway.com/pricing), Hobby fee, included usage, and the note that included usage does not accumulate, checked 2026-10-14. - Resend, [pricing](https://resend.com/pricing), Free limits and inbound counting, checked 2026-10-14. - Mailstead cost model, [pricing](/pricing/), same inputs as this example.