Deployment tooling for Xem, including a self-contained Docker Swarm starter, Terraform, and Helm. The Swarm starter runs the app, backend, PostgreSQL, Redis, and object storage on one host. The Terraform and Helm configurations support managed SMTP backed by Amazon SES, with optional payments and MCP services in the chart. The additive managed receiving stack provides private SES/S3/SNS/SQS resources for the opt-in custom-domain inbox implementation under development. Those configurations keep application settings and SMTP flags in Infisical; no credentials are committed here.
| Path | Resources owned here | Prerequisites |
|---|---|---|
| Docker Swarm starter | App, backend, PostgreSQL, Redis, and RustFS on one host | Docker Engine, Git, Python 3, OpenSSL; reachable app/API/storage addresses |
| Existing Dokploy/EC2 | Imported EC2, Elastic IP, SMTP security group, runtime role/profile, existing sending CloudFormation stack | Existing VPC/subnet, administrative SG, Dokploy, external PostgreSQL and Redis |
| Kubernetes | Workload IAM role, distinct sending stack, Helm application workloads, HTTP ingress, TCP SMTP service, optional cert-manager resources | Supported Kubernetes cluster, IAM OIDC provider, ingress controller, cert-manager, load-balancer controller, external PostgreSQL and Redis |
| Platform domain | SES platform identity, DKIM, custom MAIL FROM, optional DMARC, DNS-only SMTP record | Cloudflare authoritative zone and scoped API token |
The Kubernetes root does not provision a cluster. The EC2 root is an adoption configuration, not an unattended new-server installer. Neither path creates databases, installs controllers, approves SES production access, or deploys the static marketing website. Those have separate lifecycles. Do not point both runtimes at the same production database with sending workers active.
flowchart LR
Client[SMTP client] -->|TCP 587 + STARTTLS| Edge[EC2 published port or Kubernetes NLB]
Edge -->|TCP 2525| API[Xem backend]
API -->|TLS AWS API + runtime IAM role| SES[Amazon SES]
SES --> Inbox[Recipient mail server]
SES --> SNS[SNS delivery feedback]
SNS -->|Signed HTTPS event| API
Browser[Browser] --> Web[Frontend]
Web -->|API HTTP inside deployment| API
API --> DB[(External PostgreSQL / Redis)]
Infisical[Infisical settings] --> API
ACME[Public ACME certificate] --> API
Cloudflare provides authoritative DNS and ACME DNS challenges for Kubernetes. SMTP records must be DNS-only. Cloudflare Origin CA certificates are not trusted by ordinary SMTP clients; use a public CA such as Let's Encrypt.
swarm/: curl bootstrap support, stack generation, first-use guide, and smoke tests; runmake installfrom the repository root.terraform/environments/{dokploy,kubernetes,domain}: independent state roots with pinned providers.terraform/modules: reusable runtime identity, sending stack, and platform DNS modules.managed-receiving/: dedicated inactive SES receiving rule set, private raw-mail storage, queue, DLQ, alarms, and unattached runtime policy.charts/xem: application chart; no embedded database or secret values.hakopod/xem.toml: four-service self-hosted Hakopod configuration with public SMTP, STARTTLS readiness and automatic certificate renewal; see its setup guide.host/andscripts/install-dokploy-smtp-tls.sh: certificate export and read-only host mirror.scripts/smtp-smoke.py: transport-only test, no email or credentials.legacy/: archived Kops/manifests; not supported deployment instructions.
For a new self-hosted installation, start with the Swarm guide. For the managed infrastructure paths, start with DEPLOYMENT.md. The production snapshot records what was actually verified and the remaining delivery checks.
Use Terraform 1.16.2, Helm 3.18.6, Python 3.12 with PyYAML 6.0.2 / jsonschema 4.23.0, and kubeconform 0.7.0. Modules require Terraform >=1.10 for S3 locking. CI uses the pinned versions and requires no AWS or Cloudflare credentials.
python3 -m pip install PyYAML==6.0.2 jsonschema==4.23.0
go install github.com/yannh/kubeconform/cmd/kubeconform@v0.7.0
bash scripts/validate.sh
python3 scripts/smtp-smoke.py smtp.xem.emailThe validation script initializes providers with the backend disabled, runs plan-only IAM tests with dummy credentials and no AWS lookups, checks rendered manifests, and exercises certificate rotation with an ephemeral local CA. The optional smoke command contacts the named SMTP endpoint. Validation does not establish that images start in a real cluster or that SES accepts authenticated messages.
Contributions should include an example without secrets and validation for changed behavior. Preserve resource ownership and provider lockfiles. Pin application image digests before production use; the chart's mutable image defaults are discovery examples, not a reproducible production release.