Run small hosted mail on mailcow: plans, add-ons and billing with your own Stripe account

A practical walkthrough for turning your mailcow into a small hosted-mail service: mailbox plans and domain packages, add-ons with proration, a grace period and suspension that never deletes an account.

If you already run mailcow for yourself, you have probably been asked the same question more than once: could you also host mail for someone else? The technical part is the easy half. The hard half is billing: who is on which plan, what happens when a card fails, and how to stop an account without destroying it.

Cashier for mailcow is built for exactly that half. It sells mailbox plans and domain packages on your mailcow, paid monthly or yearly by card through your own Stripe account, and applies each change to mailcow as soon as the payment is confirmed. Unpaid accounts are suspended after a grace period you choose, never deleted. This tutorial walks through setting it up as a small hosted-mail business.

Prerequisites

Before you start, make sure you have:

  • The mailcow Toolkit package 0.4.0 or newer on mailcow-dockerized (Linux, docker compose, systemd). Cashier ships inside the package and is installed with install.sh as root on the host.
  • Sign-up for mailcow with its own valid license and set up, because paid accounts are created through its registration page.
  • A Cashier for mailcow license key (a mailcow bundle key works too) or an e-mail address for the free 30-day trial.
  • Your own Stripe account in a country Stripe supports. Money, invoices and customer data stay in your account; MediaLab Works is not a party to your sales.
  • The mailcow UI reachable from the internet over HTTPS, so Stripe can call the webhook, plus outgoing HTTPS to api.stripe.com and medialab.works for the license check.

Step 1 — Activate the license

Open System > Updates (Toolkit) and find the card License · Cashier for mailcow. Enter your key and press Activate, or start the free 30-day trial with your e-mail address. On the shell, the same thing is:

docker compose exec -u www-data php-fpm-mailcow php /web/mlw-toolkit/cli.php activate mailcow-cashier <KEY>

Run it from the mailcow folder. The license is checked at startup and once a day; without internet, Cashier keeps working for 7 days after the last successful check. Without a valid license no new checkout starts, but existing subscribers' webhooks are still processed and nothing is deleted.

Step 2 — Connect Stripe

Open System > Cashier and work through the Stripe card:

  1. In your Stripe Dashboard, create a restricted key (Developers > API keys > Create restricted key) with write access to Customers, Checkout Sessions, Customer portal, Products, Prices, Subscriptions, Subscription items and Webhook Endpoints, and read access to Invoices. A secret key works too, but a restricted key is safer.
  2. Paste the Publishable key and the Secret / restricted key. Both must be test keys or both live keys; Cashier checks them with Stripe and shows the account and the mode. The secret key is stored encrypted and never shown again, so leave the field empty later to keep it.
  3. Press Create the webhook. Cashier registers https://<mail host>/mlw-cashier/webhook.php in your Stripe account with the events it needs and saves the signing secret. If a proxy rewrites the host name, set Public webhook address under Rules first.
  4. Check Last event to confirm that events actually arrive.

To move from test to live mode later, connect the live keys and create the webhook again.

Step 3 — Define your plans

In Plans, press New plan. Choose the Kind:

  • mailbox — one mailbox with its quota, for people who only want an address.
  • domain package — for domain administrators: Domains (package), Mailboxes / domain, Aliases / domain, Quota (MB; package: per domain) and Default quota / mailbox (MB).

Then fill in the names and descriptions in English and Romanian (they appear on the registration page in the visitor's language), a monthly price and/or a yearly price in your Stripe account's currency, and trial days (0 = none). Cashier creates the product and the prices in your Stripe account for you. Leave Visible on the registration page ticked for public plans; untick it for a hidden plan reachable only through its direct link (/mlw-signup/?plan=<token>) or an invitation code bound to the plan.

Edit changes a plan: a changed amount creates a new price in Stripe and deactivates the old one, which existing subscribers keep. Archive takes a plan off sale without touching its subscribers.

Step 4 — Add add-ons

In Add-ons, create the extras customers can buy with a quantity: extra mailboxes on each domain of the package, or extra storage (MB per domain; on a mailbox plan, per mailbox). Set the Unit, the names, the monthly and yearly prices and Visible. Customers add them on My account; Stripe prorates the price and the new limits are applied to mailcow at once.

Step 5 — Set the rules: grace period and suspension

Under Rules, decide how strict you want to be:

  • Grace period after a failed payment (days, 0–90) — the default is 7 days, during which the account keeps working while the customer pays.
  • After the grace period the account is suspended — choose no sign-in: mail is still received (the default) or inactive: mailboxes, domain administrator and domains. Nothing is ever deleted, and a payment restores the account immediately.
  • E-mail the subscribers about invoices and receipts.
  • Public webhook address (empty = automatic), Stripe product tax code and Stripe name prefix — the last one matters when several mailcow servers share one Stripe account.

For a small hosting business, the default no-sign-in mode is a good starting point: a lapsed customer stops using the service, but mail keeps arriving, so nothing is lost and a payment turns everything back on.

Step 6 — Know what customers see

On the registration page, a visitor picks a plan (monthly or yearly), confirms their address and pays on Stripe's checkout page. The account is created when Stripe confirms the payment. On My account they see the plan, the next payment date and the add-ons, can use Change plan (prorated; a plan smaller than current usage is refused, with what to reduce first) and open Card, invoices and cancellation (Stripe), Stripe's customer portal.

A failed payment sends an e-mail with the link to pay and the suspension date. A suspended customer who cannot sign in can request the payment link from the public page /mlw-cashier/billing.php by entering the account's e-mail address. A cancellation takes effect at the end of the paid period, and an account created by the subscription then stays suspended.

Step 7 — Run the admin side

Subscribers lists every subscription with its state (active, grace, suspended, ended), the plan, the amount, the link to Stripe and the Monthly revenue, plus the Recent Stripe events the webhook received. Grant a plan for free gives a plan to an account for a number of days without Stripe, and a grant can be ended early — handy for testing or for a favor to a friend.

Plans, customers and subscriptions live in mailcow's database (tables mlw_ca_*), so they are part of mailcow's backups. The keys and the webhook secret sit in the Toolkit's shared folder, encrypted with AES-256-GCM, with the encryption key kept apart in mailcow's redis.

Tips and troubleshooting

  • Webhook not arriving? Check Last event in the Stripe card. If a reverse proxy rewrites the host name, set the Public webhook address under Rules and create the webhook again.
  • Keys rejected? Make sure both keys are test or both live; mixing them fails the check.
  • Customer wants a smaller plan but it is refused. That is intentional: nothing is deleted to make it fit. Ask them to remove mailboxes or storage first.
  • A repeated Stripe event. Events are signature-verified and processed once, so a duplicate changes nothing.
  • License check failing. The server must reach medialab.works over HTTPS; Cashier keeps working for 7 days after the last successful check.
  • Moving servers. uninstall.sh removes Cashier with the Toolkit but keeps the tables and every account; the encrypted keys go with the shared folder, so plan a reconnection of Stripe afterwards.

Conclusion

Cashier for mailcow does not try to be a full billing platform. It does one job well: sell mailboxes and domain packages on your own mailcow, through your own Stripe account, with prorated changes, a grace period you control and a suspension that never deletes data. Read the user guide for every field, check the changelog before updating, and use the knowledge base or FAQ when something is unclear. Questions about your setup are welcome through contact.

This post was written with AI assistance from our product documentation or the sources listed and checked automatically before publication.

Read next

Running Emby, Jellyfin or Nextcloud?

Our plugins and apps add Live TV, subtitles, sign-up and security to your own server, with a free trial.