Skip to main content
You can run Xem on your own Linux server under the GPL-3.0 licence. A one-line installer builds the app and API from the same commit and deploys them with PostgreSQL 16, Redis 7 and S3-compatible storage (RustFS) on Docker Swarm. Data lives in named Docker volumes, and the installer generates every credential for you. This page summarises the full self-hosting README, which covers every option.

Requirements

  • A Linux host with Docker Engine (including the build plugin), Git, Python 3 and OpenSSL.
  • For light use, 2 CPU cores, 2 GB RAM and 20 GB of free disk. Builds, especially the Next.js app, need extra memory or swap.
  • An IP address or hostname that both your browser and the containers can reach. Do not use localhost.
  • Free ports for the app, API and storage. The defaults are 3000, 9001 and 9000.
  • Outbound HTTPS for downloads, and access to your SMTP provider for sending.

Install

Answer the prompts with your addresses. On a trusted local network, for example: http://192.168.1.10:3000 for the app, http://192.168.1.10:9001 for the API and http://192.168.1.10:9000 for storage. For an unattended install, write a config file:
config.json
Then run:
The installer stores generated credentials, including a base64-encoded RSA key, in the state directory with owner-only permissions. It reports success only after the API health check and the app’s login page both return HTTP 200. Database migrations run automatically when the API starts.
Keep the state directory. Later runs reuse its credentials and volumes. Losing the RSA key can make previously encrypted data unreadable.

First use

1

Create your account

Open <app_url>/auth/register. There is no default admin password.
2

Connect a sender

In Settings → SMTP senders, add your SMTP provider. To use Amazon SES, follow Send with Amazon SES.
3

Authenticate your domain

Set up SPF, DKIM and DMARC with your provider before sending.
4

Test

Import contacts, pick a template and send a test to an address you control.
A self-hosted install does not include managed sending, managed inbound mail, Google login, hosted billing, the MCP server or the AI assistant. Some of these appear in the interface but need separate setup.

License

Campaigns, automations, forms, the CRM and analytics are free forever. A Xem license, $49 once from xem.email/pricing, adds newsletters, the shared inbox and Cloudflare mailboxes and removes the “Sent with Xem” footer from your emails. Dodo Payments emails the key after you buy. Add it to your install:
The installer saves the key in the state directory, so later runs keep it. You can also add "license_key" to config.json. The API checks the key when it starts and once a day. If the check can’t reach Dodo, a key that was valid stays valid.

HTTPS and public servers

The default HTTP addresses are for a trusted network. For a public server, put the app, API and storage behind a TLS reverse proxy, with addresses such as https://app.example.com, https://api.example.com and https://files.example.com. Storage requests must keep their original host, path and query so S3 signatures still match. Restrict the published ports with a firewall that applies to Docker traffic, so outside requests go through the proxy. The installer does not set up DNS, certificates, a proxy or firewall rules.

Check status and logs

All five long-running services should show one replica. The xem_storage-init service exits after creating the storage bucket, so 0/1 is expected for it.

Update

Back up first, because the API applies database migrations on start. Then run the install command again as the same user, with the same state directory. It rebuilds from the latest revision and updates the services, which can briefly interrupt service. To pin a reviewed revision:

Back up

Keep an encrypted copy of the state directory, the xem_storage volume and a PostgreSQL dump:
Stop sending during a restore so queued emails are not sent twice.

Stop

This removes the services and network but keeps the data volumes and the state directory. Run the installer again to start Xem with the same data.