# Architecture Overview **Understanding the design and structure of cdk8s-mailu deployments.** ## Introduction cdk8s-mailu is designed around the principle of **composable constructs** - small, reusable building blocks that can be combined to create complex deployments. This architecture provides flexibility while maintaining production-grade defaults. ## Component Architecture Mailu is a modular mail server composed of multiple services working together: ```mermaid graph TB Ingress[Traefik
TLS Termination] -->|Plaintext: 587, 465, 993, 995| FrontNginx[Front Nginx
Auth Proxy] Ingress -->|HTTP:80| Admin[Admin:8080
Web UI + SSO] Ingress -->|HTTP:80| Webmail[Webmail:80
Roundcube] Ingress -.MX:25.-> Postfix[Postfix:25
SMTP] FrontNginx -->|auth_http:8080/internal/auth/email| Admin FrontNginx -->|Relay SMTP:25| Postfix FrontNginx -->|Relay IMAP:143
POP3:110| Dovecot[Dovecot
IMAP/POP3] Webmail -.SMTP:10025.-> DovecotSub[Dovecot
Submission:10025] DovecotSub -.Relay:25.-> Postfix Postfix -->|Milter:11332| Rspamd[Rspamd
Spam Filter] Postfix -.Scan:3310.-> ClamAV[ClamAV
Antivirus] Postfix -->|LMTP:2525| Dovecot style DovecotSub fill:#e1f5fe style Webmail fill:#fff3e0 style Postfix fill:#c8e6c9 ``` ### Core Components **Ingress (Traefik)** - TLS termination for all protocols (HTTPS, SMTPS, IMAPS, POP3S) - Routes HTTP/HTTPS (80/443) directly to Admin and Webmail - Routes mail protocols (587, 465, 993, 995) to Front (Nginx) - Routes MX mail (port 25) directly to Postfix - LoadBalancer service with public IP **Front (Nginx)** - Protocol routing for authenticated mail protocols (SMTP submission 587/465, IMAP 993, POP3 995) - Authentication proxy using Admin's auth_http endpoint - Receives plain TCP from Traefik after TLS termination - Proxies to backend Kubernetes Services (Postfix, Dovecot) - **Note**: nginx proxies to Services, not directly to pods; if multiple backend replicas exist, the Service handles load balancing - **Note**: Port 25 (MX mail reception) and HTTP/HTTPS bypass Front entirely - Always required for authenticated mail protocols **Admin** - Web-based administration interface (accessed via Ingress at port 80 internally) - User and domain management - Configuration interface - **Authentication backend**: Provides auth_http endpoint for Front (Nginx) - **SSO service**: Single sign-on for webmail and admin interface - **Shared database**: PostgreSQL database shared with Webmail - Always enabled by default **Webmail (Roundcube)** - Browser-based email client (accessed via Ingress at port 80 internally) - Contact and calendar management - Uses Dovecot Submission service for sending mail - **Shared database**: Uses same PostgreSQL database as Admin - **SSO integration**: Authenticates via Admin's SSO service - Enabled by default, can be disabled **Postfix** - SMTP server for sending/receiving mail - Mail routing and relay - Spam/virus scanning integration - **Mail delivery**: Uses LMTP to deliver mail to Dovecot - **Port 25 (MX)**: Receives direct routing from Traefik (bypasses Front/nginx) - Traefik InFlightConn middleware: Limits simultaneous connections per IP (15 default) - Postfix anvil rate limiting: Limits connections/min (60), messages/min (100), recipients/min (300) - **Port 10025**: Internal submission relay from Dovecot submission service - Always required **Dovecot** - IMAP and POP3 server - Mail storage and retrieval - **Mail reception**: Receives mail from Postfix via LMTP - Authentication backend - Always required **Rspamd** - Spam filtering - DKIM signing/verification - Header manipulation - Always required **Dovecot Submission Service** - Dedicated service for webmail email sending - Uses official `dovecot/dovecot:2.3-latest` image - Listens on port 10025 for token authentication - Relays to Postfix:25 using `submission_relay_host` - Solves configuration issues with bundled dovecot in front container - Always deployed (required for webmail functionality) ### Optional Components **ClamAV** - Antivirus scanning for attachments - Virus definition updates - Resource-intensive (requires ~1GB RAM) - Disabled by default **Webdav (Radicale)** - CalDAV and CardDAV server - Calendar and contact synchronization - Disabled by default **Fetchmail** - Fetch email from external POP3/IMAP servers - Consolidate multiple accounts - Disabled by default ## Mail Delivery Flow Details Understanding how mail flows through the system is crucial for troubleshooting and configuration. ### Inbound Mail (Internet → Mailbox) **Complete flow from external sender to user mailbox:** 1. **External MTA → Traefik:25** - Internet mail server sends to your MX record 2. **Traefik → Postfix:25** - Traefik routes directly to Postfix (bypasses Front/Nginx) 3. **Postfix → Rspamd:11332** - Postfix calls Rspamd via milter protocol for spam scanning 4. **Rspamd → Postfix** - Rspamd returns scan results (accept/reject/add headers) 5. **Postfix → Dovecot:2525** - Postfix delivers mail to Dovecot via LMTP protocol 6. **Dovecot stores** - Mail written to `/mail` PVC in Maildir format **Key Points**: - Rspamd scanning is **inline** (mail passes through), not parallel - Port 25 (MX) requires no authentication (standard SMTP behavior) - ClamAV scanning happens during Rspamd step if enabled - LMTP delivery ensures reliable handoff from Postfix to Dovecot **Rate Limiting**: - Traefik InFlightConn middleware: Limits simultaneous connections per IP - Postfix anvil: Connection rate (60/min), message rate (100/min), recipient rate (300/min) ### Webmail Sending (User → Internet via Webmail) **Complete flow when user sends email via webmail:** 1. **User → Browser → Webmail:80** - User composes email in Roundcube 2. **Webmail authenticates** - User already authenticated via Admin SSO 3. **Webmail → Dovecot-Submission:10025** - Webmail sends via dedicated submission service 4. **Dovecot-Submission accepts** - Uses `nopassword=y` trust model (network isolation) 5. **Dovecot-Submission → Postfix:25** - Relays to Postfix without authentication 6. **Postfix → Internet** - Delivers to recipient's MX server **Key Points**: - "Token authentication" means webmail session trust, not actual token validation - Trust based on **network isolation** (only webmail pod can reach dovecot-submission:10025) - Dovecot-submission is a relay-only service (no mailbox access) - Bypasses Front/Nginx entirely for direct backend communication **Why separate submission service?** - Webmail needs to send mail without user entering password again - Session-based trust is more secure than storing passwords - Isolates webmail submission from regular SMTP submission ### Authenticated SMTP/IMAP (Mail Client → Server) **Complete flow for authenticated mail client access:** 1. **Mail client → Traefik:587/993/995** - Client connects (Thunderbird, Outlook, etc.) 2. **Traefik TLS termination** - Traefik decrypts TLS, forwards plaintext 3. **Traefik → Front:587/993/995** - Nginx receives plaintext connection 4. **Front → Admin:8080/internal/auth/email** - Nginx auth_http check 5. **Admin validates** - Queries PostgreSQL for user credentials 6. **Admin → Front** - Returns HTTP 200 (success) or 403 (failure) with backend address 7. **If authenticated**: - SMTP (587/465): Front → Postfix:25 - IMAP (993/143): Front → Dovecot:143 - POP3 (995/110): Front → Dovecot:110 **Key Points**: - All TLS ports receive **plaintext** traffic (TLS_FLAVOR=notls, Traefik handles TLS) - Nginx auth_http protocol validates every connection - Front acts as authentication proxy, not just protocol router - Port 25 (MX) bypasses authentication (accepts from any sender) ### Service Discovery and FRONT_ADDRESS > **⚠️ IMPORTANT: FRONT_ADDRESS Naming Quirk** > > The environment variable `FRONT_ADDRESS` is **misleading** - despite its name, it points to the **Dovecot service**, NOT the Front (Nginx) service. > > **Why?** This is a Mailu upstream naming convention. Mailu historically used the "front" container for LMTP delivery, so the variable is named `FRONT_ADDRESS`. In cdk8s-mailu architecture with Traefik TLS termination, we point this variable to Dovecot directly for LMTP delivery from Postfix. > > **Code reference**: `src/mailu-chart.ts:384-388` > ```typescript > // FRONT_ADDRESS is used for LMTP delivery (postfix -> dovecot:2525) > // Despite the name, it should point to dovecot, not the nginx front service > if (this.dovecotConstruct?.service) { > this.sharedConfigMap.addData('FRONT_ADDRESS', > `${this.dovecotConstruct.service.name}.${namespace}.svc.cluster.local`); > } > ``` > > **Impact**: When debugging, remember that `FRONT_ADDRESS` = Dovecot service DNS name, not Front service. ## CDK8S Design Patterns ### Construct Hierarchy ``` MailuChart (extends Chart) ├── Namespace ├── SharedConfigMap (service discovery) ├── NginxPatchConfigMap (TLS_FLAVOR=notls wrapper) ├── WebmailPatchConfigMap (backend connection patches) ├── FrontConstruct │ ├── Deployment (nginx with wrapper script) │ └── Service (HTTP, SMTP, IMAP, POP3 ports) ├── AdminConstruct │ ├── Deployment │ ├── Service │ └── PersistentVolumeClaim (5Gi) ├── PostfixConstruct │ ├── Deployment │ ├── Service (port 25, 10025) │ └── PersistentVolumeClaim (5Gi) ├── DovecotConstruct │ ├── Deployment │ ├── Service │ └── PersistentVolumeClaim (mailbox storage) ├── DovecotSubmissionConstruct │ ├── Deployment (AMD64 nodeSelector) │ ├── Service (port 10025) │ └── ConfigMap (dovecot.conf, entrypoint.sh) ├── RspamdConstruct │ ├── Deployment │ ├── Service │ └── PersistentVolumeClaim (5Gi) └── Optional: ├── WebmailConstruct ├── ClamavConstruct ├── FetchmailConstruct └── WebdavConstruct ``` Each construct is **self-contained** and manages: - Kubernetes resources (Deployment, Service, ConfigMap, etc.) - Resource requirements (CPU, memory) - Volume mounts and storage - Environment variables - Service discovery configuration ### Configuration Flow 1. **User provides MailuConfig** - Type-safe configuration object 2. **MailuChart validates config** - Ensures required fields present 3. **Shared resources created** - Namespace, shared ConfigMap 4. **Constructs instantiated conditionally** - Based on component toggles 5. **Resources synthesized** - CDK8S generates Kubernetes YAML ### Resource Management Philosophy **Defaults optimized for production:** - Conservative resource requests (pods scheduled reliably) - Higher limits (allow bursting for traffic spikes) - Based on real-world usage patterns - Can be overridden per-component **Example:** Admin component - Request: 100m CPU, 256Mi memory (guaranteed minimum) - Limit: 300m CPU, 512Mi memory (burstable maximum) ## Storage Architecture ### Persistent Volumes **Data Volume** (`/data`) - Application data, SQLite database (if used) - Configuration files - Default: 10Gi **Mail Volume** (`/mail`) - User mailboxes and messages - Largest storage requirement - Default: 50Gi, adjust based on users ### Database Options **SQLite (Default)** - Simple, zero-configuration - Suitable for small deployments (<100 users) - Stored in `/data` volume **PostgreSQL (Recommended for Production)** - Better performance and reliability - Required for high-availability setups - Managed separately (CNPG, cloud database, etc.) ## Network Architecture ### Service Discovery All components communicate via Kubernetes services: - `{component-name}-service` - Standard naming pattern - Internal DNS resolution - No external dependencies for inter-component communication ### Ingress/TLS Configuration **Traefik TLS Termination (Supported & Tested)** - Traefik handles TLS for all protocols (SMTP/IMAP/HTTP/HTTPS) - Nginx wrapper patches Front component for plaintext backend communication - Automatic certificate management via cert-manager - This is the **only tested and supported configuration** in cdk8s-mailu **Alternative Ingress Solutions** - Theoretically, other ingress controllers could be used (e.g., nginx-ingress, HAProxy) - Would require custom configuration and testing - **Not tested or documented** - use at your own risk **Front Direct TLS (Not Recommended)** - Mailu's Front component can handle TLS directly (TLS_FLAVOR=cert, letsencrypt, mail, etc.) - **This configuration is untested in cdk8s-mailu** and likely requires significant modifications - Manual certificate management, no nginx patching applied - Not recommended: use Traefik termination instead ## Design Decisions ### Why CDK8S? CDK8S was chosen over traditional YAML/Helm for cdk8s-mailu because it provides **infrastructure as code** benefits that are crucial for maintaining a complex, multi-component mail server deployment. **Type Safety Prevents Configuration Errors** - Catch mistakes at compile time, not deploy time - IDE shows available options with autocomplete - Impossible to reference non-existent fields - Refactoring is safe and reliable **Real Example**: When the dovecot submission service was added, TypeScript's type system immediately caught everywhere that needed updates (service discovery, ConfigMap, construct exports). With YAML, these would have been runtime failures. **Programmatic Logic Simplifies Complex Configurations** - Conditional resource creation based on config flags - Dynamic service name generation with hashing - Environment variable construction from multiple sources - Resource calculation (e.g., convert "256Mi" to bytes) **Testability Ensures Reliability** - Unit tests for individual constructs - Integration tests for complete deployments - Test coverage >90% achieved - Snapshot testing for manifest stability Advantages: - Type-safe configuration with compile-time validation - IDE autocomplete and inline documentation - Programmatic manifest generation with logic - Testable infrastructure code (>90% coverage) - Reusable constructs across projects ### Why Modular Constructs? Each Mailu component is implemented as a separate construct class (AdminConstruct, PostfixConstruct, etc.) rather than one monolithic MailuChart. This modular approach was essential for managing complexity. **Independent Testing and Development** - Each construct has its own test file - Can develop and test components in isolation - Easier to debug when issues arise - Regression testing catches component-specific breaks **Flexible Configuration** - Enable/disable components with simple flags - Override resources per-component - Customize storage per-service - Different image tags per component possible **Real Example**: The dovecot submission service was added as a new DovecotSubmissionConstruct without touching existing constructs. If it had been one big class, the change would have affected every test and increased risk. **Clear Ownership and Documentation** - Each construct file is self-documenting - Props interface shows required configuration - Construct methods are single-purpose - Easy to understand data flow Benefits: - Component-level customization (resources, storage, images) - Easier testing (unit test each construct independently) - Clear separation of concerns (one construct = one service) - Maintainability (changes isolated to specific constructs) - Parallel development possible ### Why Production Defaults? cdk8s-mailu is designed with **opinionated production-grade defaults** rather than minimal examples that require extensive configuration. This reduces the "time to working deployment" and prevents common misconfigurations. **Resource Requests and Limits** - All components have sensible CPU/memory defaults - Requests ensure reliable scheduling - Limits prevent resource exhaustion - Based on real production metrics **Example Defaults**: - Admin: 100m CPU / 256Mi RAM (adequate for <1000 users) - Postfix: 100m CPU / 512Mi RAM (handles typical mail volume) - Dovecot: 200m CPU / 1Gi RAM (IMAP is memory-intensive) **Storage Sizes** - Admin PVC: 5Gi (configuration and SQLite if used) - Postfix PVC: 5Gi (mail queue) - Dovecot PVC: 50Gi default (adjust based on users) - Rspamd PVC: 5Gi (spam filter data) **Security and Reliability** - Non-root filesystem disabled where needed (mail services require privileges) - Health probes configured automatically - Service discovery via environment variables - PVC retention policies appropriate for mail data **Real-World Validation** - Defaults tested on production clusters - Successfully deployed with AMD64/ARM64 mixed nodes - Handles real email traffic and webmail usage - Proven to work with Traefik TLS termination Philosophy: - "Works out of the box" for most use cases (minimal configuration required) - Override only what you need (but you can customize everything) - Based on real-world production deployments - Fail-safe rather than fail-fast (conservative resources, not minimal) ## See Also - [Dovecot Submission Service](dovecot-submission.md) - Detailed explanation of webmail email sending - [CDK8S Patterns](cdk8s-patterns.md) - Construct design patterns - [Configuration Options](../reference/configuration-options.md) - Complete API reference - [Quick Start Tutorial](../tutorials/01-quick-start.md) - Deploy your first instance