DarDev Unified Mailer is the self-hosted stack we run for CRM, marketing email, and transactional SMTP—not a slide deck architecture. It connects Twenty CRM, Listmonk campaigns, Stalwart MTA, a sync worker, and public properties like news.dardev.net on infrastructure we operate daily on dardev-vps.
We publish this breakdown under Deployed Realities: if a pattern is not good enough for our own outreach and product lists, we do not recommend it as enterprise-grade to clients.
Components and roles
- Twenty CRM (crm.dardev.net): contacts, segments, API keys, workspace permissions
- Listmonk (mail.dardev.net): product lists, campaigns, subscribe forms, analytics
- Stalwart MTA (mta.dardev.net / send.dardev.net): SMTP submission, DKIM, outbound identity
- Sync worker: Twenty webhooks → Listmonk list membership with opt-in rules
- PostgreSQL + Redis: shared persistence and queues behind Docker Compose
Data flow: CRM to campaign
Cold outreach prospects land in Twenty first—OECT accountants, Startup Tunisia SMB, DarDev Services tracks—without automatic Listmonk marketing tags. When a contact opts in (form, explicit consent field, or product interest flag), the sync worker maps CRM attributes to the correct Listmonk list using PRODUCT_LIST_MAP configuration.
Why self-host
Control over consent logs, CRM segmentation, and SMTP reputation matters for B2B outreach in Tunisia and MENA. SaaS bundles are faster to start but weaker when you need custom sync logic between CRM tags and multiple product lists (Hesabi, DarDevLab, EscaHire waitlist).
Deployment shape on dardev-vps
Services bind to localhost ports behind host nginx TLS: Twenty on 3001, Listmonk on 9002, sync worker health on 3100, Stalwart admin on 8080. Compose uses docker-compose.yml plus docker-compose.dardev-vps.yml overlay alongside other stacks—host nginx terminates TLS on dardev-vps.
Operational practices we enforce
- Double opt-in for public subscribe forms on news.dardev.net
- API rate limits patched in Postgres for Twenty bulk import jobs
- Separate Hesabi CRM views per outreach track—no duplicate cold email blasts
- Health endpoint on sync worker monitored before campaigns send

What this stack is not
It is not a replacement for Hesabi fiscal invoicing or TTN clearance. Twenty holds relationship and consent data; Hesabi holds Tunisian SME finance. EscaHire remains waitlist—not wired into production campaigns as GA product.
DarDev News static site and subscribe API also route through this infrastructure, giving us end-to-end dogfooding from content to CRM to mailer.
Security and secrets
API keys for Twenty and Listmonk live in environment files on the VPS—not in git. Webhook endpoints validate signatures where supported. Postgres backups include CRM and list data subject to consent obligations.
Admin UIs sit behind TLS on subdomains; we do not iframe CRM inside dashboards (known stability issues). Operators use direct URLs with workspace auth.
When scaling outreach, watch Stalwart queue depth and Listmonk bounce rates together—CRM imports mean nothing if SMTP reputation collapses from stale lists.
Future evolution may add more product-specific lists and automation, but opt-in gating remains non-negotiable for marketing sends.
Comparison to all-in-one SaaS marketing suites
HubSpot-style bundles optimize for speed to first campaign but weak custom CRM fields for multi-product consent tracking. Our split stack costs more ops time upfront and pays back when Hesabi, DarDevLab, and EscaHire lists need different legal bases.
Sync worker code is small but explicit: webhook handlers, idempotency keys, and health checks you can audit. That transparency matters for privacy-conscious B2B buyers asking how CRM tags become email lists.
Runbooks live in this repo under docs/runbooks—operators can redeploy from Windows via WSL scripts matching how DarDev engineers work daily.
If you replicate this architecture, start with consent rules and DNS on send.dardev.net before importing prospect CSVs—reputation damage outlasts quick list growth.
Monitoring hooks include Listmonk campaign stats, Stalwart bounce logs, and Twenty API health—operators review weekly before large outreach waves.
DarDev News subscribe forms hit sync-worker health checks documented in verify-news.sh; public site and mailer stack evolve together.
Quick reference URLs (internal ops)
- crm.dardev.net — Twenty workspace
- mail.dardev.net — Listmonk admin
- mta.dardev.net — Stalwart admin and SMTP hostname
- news.dardev.net — public content and subscribe API
Compose files live under /opt/dardev-mailer on dardev-vps; deploy scripts sync from this monorepo without committing secrets.
Readers implementing similar stacks should clone the pattern, not the credentials—rotate all keys and use their own sending subdomain with proper DNS.
Cold outreach imports (Hesabi OECT, Startup Tunisia, DarDev Services) stay CRM-only until explicit opt-in—documented in outreach playbooks in this monorepo for operator consistency.
Twenty dashboard widgets for outreach segments complement but do not replace Listmonk deliverability monitoring—use both views before scaling campaigns.
Validate-all.sh and verify-news.sh scripts in this repo are part of how operators confirm the stack after each deploy—not optional niceties.
Operators rotate on-call for mailer incidents separately from Hesabi fiscal support—different skills, different escalation paths.
Can I buy Unified Mailer as a product?
It is our internal reference architecture; DarDev Services can implement similar stacks for clients—contact via dardev.net.
Why Twenty plus Listmonk?
Twenty for flexible CRM data model; Listmonk for high-volume campaign delivery and list hygiene.
Transactional vs marketing mail?
Same Stalwart instance, different From domains and templates; consent rules differ.
Open source?
Twenty, Listmonk, and Stalwart are open-source cores—we self-host and customize sync logic.
Where to start reading?
See deployed-realities-explained and crm-driven-product-lists on this site.



