Add snnapymail and stalwart
This commit is contained in:
parent
88206b6bde
commit
a962db73c8
529
MAIL_STACK_README.md
Normal file
529
MAIL_STACK_README.md
Normal file
@ -0,0 +1,529 @@
|
||||
# Lightweight Mail Stack for k3s GitOps Lab
|
||||
|
||||
Complete deployment of **Stalwart Mail Server** + **SnappyMail** webmail using GitOps with ArgoCD.
|
||||
|
||||
## 📋 Overview
|
||||
|
||||
This repository deploys a lightweight, modern mail stack designed for self-hosting and lab environments:
|
||||
|
||||
- **Stalwart Mail Server**: All-in-one mail server (SMTP, IMAP, Admin UI)
|
||||
- **SnappyMail**: Modern, lightweight webmail client
|
||||
- **GitOps**: Managed by ArgoCD
|
||||
- **Storage**: NFS-based persistent volumes
|
||||
- **Ingress**: Traefik for web UI access
|
||||
|
||||
## 🏗️ Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ Traefik Ingress │
|
||||
│ mail.dvirlabs.com webmail.dvirlabs.com │
|
||||
└───────────┬──────────────────────┬──────────────┘
|
||||
│ │
|
||||
│ │
|
||||
┌───────▼────────┐ ┌────────▼────────┐
|
||||
│ Stalwart │◄───│ SnappyMail │
|
||||
│ Mail Server │ │ Webmail │
|
||||
│ │ │ │
|
||||
│ • SMTP │ │ Connects via: │
|
||||
│ • IMAP │ │ • IMAP:993 │
|
||||
│ • Admin UI │ │ • SMTP:587 │
|
||||
└────────┬───────┘ └─────────────────┘
|
||||
│
|
||||
│
|
||||
┌────────▼───────┐
|
||||
│ NFS Storage │
|
||||
│ Mail Data │
|
||||
└────────────────┘
|
||||
```
|
||||
|
||||
## 📁 Repository Structure
|
||||
|
||||
```
|
||||
mail-services/
|
||||
├── argocd-apps/
|
||||
│ ├── stalwart.yaml # ArgoCD Application for Stalwart
|
||||
│ └── snappymail.yaml # ArgoCD Application for SnappyMail
|
||||
├── charts/
|
||||
│ ├── stalwart/ # Local Helm chart for Stalwart
|
||||
│ │ ├── Chart.yaml
|
||||
│ │ ├── values.yaml # Default values
|
||||
│ │ └── templates/
|
||||
│ │ ├── namespace.yaml
|
||||
│ │ ├── secret.yaml
|
||||
│ │ ├── statefulset.yaml
|
||||
│ │ ├── service.yaml
|
||||
│ │ └── ingress.yaml
|
||||
│ └── snappymail/ # Local Helm chart for SnappyMail
|
||||
│ ├── Chart.yaml
|
||||
│ ├── values.yaml # Default values
|
||||
│ └── templates/
|
||||
│ ├── deployment.yaml
|
||||
│ ├── pvc.yaml
|
||||
│ ├── service.yaml
|
||||
│ ├── ingress.yaml
|
||||
│ └── configmap.yaml
|
||||
└── manifests/
|
||||
├── stalwart/
|
||||
│ └── values.yaml # Custom values for dvirlabs.com
|
||||
└── snappymail/
|
||||
└── values.yaml # Custom values for dvirlabs.com
|
||||
```
|
||||
|
||||
## 🚀 Quick Start
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- k3s cluster running
|
||||
- ArgoCD installed and configured
|
||||
- Traefik ingress controller
|
||||
- NFS storage class (`nfs-client`)
|
||||
- DNS records pointing to your cluster
|
||||
|
||||
### Step 1: Update Configuration
|
||||
|
||||
1. **Update ArgoCD Application manifests** with your Git repository URL:
|
||||
|
||||
```bash
|
||||
# Edit both files and replace YOUR_USERNAME with your actual repo
|
||||
vim argocd-apps/stalwart.yaml
|
||||
vim argocd-apps/snappymail.yaml
|
||||
```
|
||||
|
||||
2. **Change the Stalwart admin password**:
|
||||
|
||||
```bash
|
||||
# Edit and set a strong password
|
||||
vim manifests/stalwart/values.yaml
|
||||
```
|
||||
|
||||
Find this section and change `CHANGE_ME_PLEASE_USE_STRONG_PASSWORD`:
|
||||
|
||||
```yaml
|
||||
secret:
|
||||
create: true
|
||||
name: stalwart-credentials
|
||||
adminPassword: "YOUR_STRONG_PASSWORD_HERE"
|
||||
```
|
||||
|
||||
3. **Update domain names** (if not using dvirlabs.com):
|
||||
|
||||
```bash
|
||||
# Update in both files
|
||||
vim manifests/stalwart/values.yaml
|
||||
vim manifests/snappymail/values.yaml
|
||||
```
|
||||
|
||||
### Step 2: Deploy with ArgoCD
|
||||
|
||||
```bash
|
||||
# Apply ArgoCD Applications
|
||||
kubectl apply -f argocd-apps/stalwart.yaml
|
||||
kubectl apply -f argocd-apps/snappymail.yaml
|
||||
|
||||
# Check deployment status
|
||||
kubectl get applications -n argocd
|
||||
|
||||
# Watch pods come up
|
||||
kubectl get pods -n mail -w
|
||||
```
|
||||
|
||||
### Step 3: Verify Deployment
|
||||
|
||||
```bash
|
||||
# Check all resources in mail namespace
|
||||
kubectl get all -n mail
|
||||
|
||||
# Check PVCs
|
||||
kubectl get pvc -n mail
|
||||
|
||||
# Check ingresses
|
||||
kubectl get ingress -n mail
|
||||
```
|
||||
|
||||
Expected output:
|
||||
```
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
pod/stalwart-0 1/1 Running 0 2m
|
||||
pod/snappymail-xxx-xxx 1/1 Running 0 2m
|
||||
|
||||
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
|
||||
service/stalwart ClusterIP 10.43.x.x <none> 8080/TCP,25/TCP,587/TCP,993/TCP
|
||||
service/snappymail ClusterIP 10.43.x.x <none> 8888/TCP
|
||||
|
||||
NAME CLASS HOSTS
|
||||
ingress.networking.k8s.io/stalwart traefik mail.dvirlabs.com
|
||||
ingress.networking.k8s.io/snappymail traefik webmail.dvirlabs.com
|
||||
```
|
||||
|
||||
## 🌐 Access the Services
|
||||
|
||||
### Stalwart Admin UI
|
||||
|
||||
URL: `https://mail.dvirlabs.com`
|
||||
|
||||
Default credentials:
|
||||
- Username: `admin@dvirlabs.com`
|
||||
- Password: (the one you set in manifests/stalwart/values.yaml)
|
||||
|
||||
### SnappyMail Webmail
|
||||
|
||||
URL: `https://webmail.dvirlabs.com`
|
||||
|
||||
First-time setup:
|
||||
1. Access the admin panel: `https://webmail.dvirlabs.com/?admin`
|
||||
2. Default admin password: `12345` (change immediately!)
|
||||
3. Configure mail server connection:
|
||||
- **IMAP Server**: `stalwart.mail.svc.cluster.local`
|
||||
- **IMAP Port**: `993`
|
||||
- **IMAP Security**: SSL/TLS
|
||||
- **SMTP Server**: `stalwart.mail.svc.cluster.local`
|
||||
- **SMTP Port**: `587`
|
||||
- **SMTP Security**: STARTTLS
|
||||
|
||||
## 📧 Configuring Real Mail Service
|
||||
|
||||
### Important: Cloudflare Tunnel Limitations
|
||||
|
||||
⚠️ **WARNING**: While Cloudflare Tunnel works fine for web UIs (admin panel and webmail), it **CANNOT** be used for actual email protocols (SMTP/IMAP).
|
||||
|
||||
**What works through Cloudflare Tunnel:**
|
||||
- ✅ Stalwart admin UI (HTTPS)
|
||||
- ✅ SnappyMail webmail (HTTPS)
|
||||
|
||||
**What does NOT work through Cloudflare Tunnel:**
|
||||
- ❌ Receiving mail from other servers (SMTP port 25)
|
||||
- ❌ Sending mail to other servers (SMTP port 25)
|
||||
- ❌ External email clients (IMAP/SMTP)
|
||||
|
||||
### Required for Real Email
|
||||
|
||||
To receive and send real email, you need:
|
||||
|
||||
#### 1. DNS Records
|
||||
|
||||
```dns
|
||||
; MX Record (Mail Exchange)
|
||||
@ IN MX 10 mail.dvirlabs.com.
|
||||
|
||||
; A Record (pointing to your public IP - NOT Cloudflare Tunnel)
|
||||
mail IN A YOUR_PUBLIC_IP
|
||||
|
||||
; SPF Record (Sender Policy Framework)
|
||||
@ IN TXT "v=spf1 mx ~all"
|
||||
|
||||
; DMARC Record
|
||||
_dmarc IN TXT "v=DMARC1; p=quarantine; rua=mailto:admin@dvirlabs.com"
|
||||
|
||||
; DKIM Record (generated by Stalwart)
|
||||
; Get this from Stalwart admin UI after setup
|
||||
default._domainkey IN TXT "v=DKIM1; k=rsa; p=YOUR_PUBLIC_KEY_HERE"
|
||||
```
|
||||
|
||||
#### 2. Port Forwarding
|
||||
|
||||
You need to expose these ports directly (NOT through Cloudflare):
|
||||
|
||||
```
|
||||
Port 25 (SMTP) - Required for receiving mail from other servers
|
||||
Port 587 (SMTP) - Required for sending mail (submission)
|
||||
Port 465 (SMTPS) - Optional, secure SMTP submission
|
||||
Port 993 (IMAPS) - Required for IMAP access
|
||||
Port 143 (IMAP) - Optional, plaintext IMAP
|
||||
```
|
||||
|
||||
**Option A: NodePort Service**
|
||||
|
||||
```bash
|
||||
kubectl apply -f - <<EOF
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: stalwart-external
|
||||
namespace: mail
|
||||
spec:
|
||||
type: NodePort
|
||||
ports:
|
||||
- name: smtp
|
||||
port: 25
|
||||
targetPort: 25
|
||||
nodePort: 30025
|
||||
- name: submission
|
||||
port: 587
|
||||
targetPort: 587
|
||||
nodePort: 30587
|
||||
- name: imaps
|
||||
port: 993
|
||||
targetPort: 993
|
||||
nodePort: 30993
|
||||
selector:
|
||||
app.kubernetes.io/name: stalwart
|
||||
EOF
|
||||
```
|
||||
|
||||
Then forward ports 25, 587, 993 from your router to your k3s node on ports 30025, 30587, 30993.
|
||||
|
||||
**Option B: LoadBalancer with MetalLB**
|
||||
|
||||
If you have MetalLB configured:
|
||||
|
||||
```bash
|
||||
kubectl apply -f - <<EOF
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: stalwart-lb
|
||||
namespace: mail
|
||||
spec:
|
||||
type: LoadBalancer
|
||||
loadBalancerIP: YOUR_LB_IP
|
||||
ports:
|
||||
- name: smtp
|
||||
port: 25
|
||||
targetPort: 25
|
||||
- name: submission
|
||||
port: 587
|
||||
targetPort: 587
|
||||
- name: imaps
|
||||
port: 993
|
||||
targetPort: 993
|
||||
selector:
|
||||
app.kubernetes.io/name: stalwart
|
||||
EOF
|
||||
```
|
||||
|
||||
#### 3. PTR (Reverse DNS) Record
|
||||
|
||||
Contact your ISP or VPS provider to set a PTR record:
|
||||
|
||||
```
|
||||
YOUR_PUBLIC_IP -> mail.dvirlabs.com
|
||||
```
|
||||
|
||||
This is **critical** for email deliverability. Without it, many servers will reject your mail.
|
||||
|
||||
## 🔧 Configuration Management
|
||||
|
||||
### Using External Secrets (Recommended for Production)
|
||||
|
||||
Instead of storing passwords in Git, use External Secrets Operator:
|
||||
|
||||
1. Install External Secrets Operator
|
||||
2. Create a secret in your secret backend (Vault, AWS Secrets Manager, etc.)
|
||||
3. Update manifests/stalwart/values.yaml:
|
||||
|
||||
```yaml
|
||||
secret:
|
||||
create: false # Don't create the secret
|
||||
name: stalwart-credentials # Reference external secret
|
||||
```
|
||||
|
||||
4. Create an ExternalSecret:
|
||||
|
||||
```yaml
|
||||
apiVersion: external-secrets.io/v1beta1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: stalwart-credentials
|
||||
namespace: mail
|
||||
spec:
|
||||
refreshInterval: 1h
|
||||
secretStoreRef:
|
||||
name: your-secret-store
|
||||
kind: SecretStore
|
||||
target:
|
||||
name: stalwart-credentials
|
||||
data:
|
||||
- secretKey: STALWART_ADMIN_PASSWORD
|
||||
remoteRef:
|
||||
key: mail/stalwart/admin-password
|
||||
```
|
||||
|
||||
## 🛠️ Maintenance
|
||||
|
||||
### View Stalwart Logs
|
||||
|
||||
```bash
|
||||
kubectl logs -n mail stalwart-0 -f
|
||||
```
|
||||
|
||||
### View SnappyMail Logs
|
||||
|
||||
```bash
|
||||
kubectl logs -n mail -l app.kubernetes.io/name=snappymail -f
|
||||
```
|
||||
|
||||
### Access Stalwart Shell
|
||||
|
||||
```bash
|
||||
kubectl exec -it -n mail stalwart-0 -- /bin/sh
|
||||
```
|
||||
|
||||
### Backup Mail Data
|
||||
|
||||
```bash
|
||||
# Backup Stalwart data
|
||||
kubectl exec -n mail stalwart-0 -- tar czf /tmp/mail-backup.tar.gz /opt/stalwart-mail
|
||||
kubectl cp mail/stalwart-0:/tmp/mail-backup.tar.gz ./mail-backup-$(date +%Y%m%d).tar.gz
|
||||
|
||||
# Backup SnappyMail config
|
||||
kubectl exec -n mail -l app.kubernetes.io/name=snappymail -- tar czf /tmp/snappymail-backup.tar.gz /var/lib/snappymail
|
||||
kubectl cp mail/snappymail-xxx:/tmp/snappymail-backup.tar.gz ./snappymail-backup-$(date +%Y%m%d).tar.gz
|
||||
```
|
||||
|
||||
### Restore from Backup
|
||||
|
||||
```bash
|
||||
# Restore Stalwart
|
||||
kubectl cp ./mail-backup.tar.gz mail/stalwart-0:/tmp/mail-backup.tar.gz
|
||||
kubectl exec -n mail stalwart-0 -- tar xzf /tmp/mail-backup.tar.gz -C /
|
||||
kubectl rollout restart statefulset -n mail stalwart
|
||||
```
|
||||
|
||||
## 🔍 Troubleshooting
|
||||
|
||||
### Pods Not Starting
|
||||
|
||||
```bash
|
||||
# Check pod events
|
||||
kubectl describe pod -n mail stalwart-0
|
||||
kubectl describe pod -n mail -l app.kubernetes.io/name=snappymail
|
||||
|
||||
# Check PVC status
|
||||
kubectl get pvc -n mail
|
||||
```
|
||||
|
||||
### Ingress Not Working
|
||||
|
||||
```bash
|
||||
# Check ingress status
|
||||
kubectl describe ingress -n mail
|
||||
|
||||
# Check Traefik logs
|
||||
kubectl logs -n kube-system -l app.kubernetes.io/name=traefik
|
||||
|
||||
# Test internal connectivity
|
||||
kubectl run -it --rm debug --image=busybox -n mail -- wget -O- http://stalwart:8080
|
||||
```
|
||||
|
||||
### SnappyMail Can't Connect to Stalwart
|
||||
|
||||
```bash
|
||||
# Test IMAP connectivity from SnappyMail pod
|
||||
kubectl exec -it -n mail -l app.kubernetes.io/name=snappymail -- nc -zv stalwart.mail.svc.cluster.local 993
|
||||
|
||||
# Check Stalwart service
|
||||
kubectl get svc -n mail stalwart
|
||||
```
|
||||
|
||||
### Email Not Being Delivered
|
||||
|
||||
Common issues:
|
||||
1. **No PTR record**: Check reverse DNS
|
||||
2. **Port 25 blocked**: Many ISPs block outbound port 25
|
||||
3. **Missing SPF/DKIM/DMARC**: Check DNS records
|
||||
4. **IP on blacklist**: Check https://mxtoolbox.com/blacklists.aspx
|
||||
|
||||
## 📊 Monitoring
|
||||
|
||||
### Check Mail Queue
|
||||
|
||||
```bash
|
||||
# Access Stalwart admin UI
|
||||
# https://mail.dvirlabs.com
|
||||
# Navigate to Queue section
|
||||
```
|
||||
|
||||
### Resource Usage
|
||||
|
||||
```bash
|
||||
# Check pod resource usage
|
||||
kubectl top pods -n mail
|
||||
|
||||
# Check PVC usage
|
||||
kubectl exec -n mail stalwart-0 -- df -h /opt/stalwart-mail
|
||||
```
|
||||
|
||||
## 🔐 Security Hardening
|
||||
|
||||
### Recommended Post-Deployment Steps
|
||||
|
||||
1. **Change SnappyMail admin password** immediately
|
||||
2. **Enable fail2ban** for brute force protection
|
||||
3. **Set up TLS certificates** with cert-manager (if not using Cloudflare)
|
||||
4. **Enable DKIM signing** in Stalwart
|
||||
5. **Configure rate limiting** in Stalwart
|
||||
6. **Regular backups** of mail data
|
||||
7. **Monitor logs** for suspicious activity
|
||||
|
||||
### TLS Certificates with cert-manager
|
||||
|
||||
If you want Let's Encrypt certificates instead of Cloudflare:
|
||||
|
||||
```yaml
|
||||
# Add to ingress annotations
|
||||
cert-manager.io/cluster-issuer: letsencrypt-prod
|
||||
```
|
||||
|
||||
And configure TLS:
|
||||
|
||||
```yaml
|
||||
tls:
|
||||
- hosts:
|
||||
- mail.dvirlabs.com
|
||||
secretName: stalwart-tls
|
||||
```
|
||||
|
||||
## 📚 Additional Resources
|
||||
|
||||
- [Stalwart Documentation](https://stalw.art/docs)
|
||||
- [SnappyMail Documentation](https://snappymail.eu/)
|
||||
- [Email Deliverability Best Practices](https://www.mail-tester.com/)
|
||||
- [DKIM Setup Guide](https://www.cloudflare.com/learning/dns/dns-records/dns-dkim-record/)
|
||||
|
||||
## ⚙️ Customization
|
||||
|
||||
### Adjust Storage Size
|
||||
|
||||
Edit `manifests/stalwart/values.yaml` or `manifests/snappymail/values.yaml`:
|
||||
|
||||
```yaml
|
||||
persistence:
|
||||
size: 50Gi # Increase as needed
|
||||
```
|
||||
|
||||
### Change Resource Limits
|
||||
|
||||
Edit `manifests/*/values.yaml`:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
requests:
|
||||
memory: "2Gi"
|
||||
cpu: "1000m"
|
||||
limits:
|
||||
memory: "8Gi"
|
||||
cpu: "4000m"
|
||||
```
|
||||
|
||||
### Add Multiple Domains
|
||||
|
||||
Configure in Stalwart admin UI after deployment.
|
||||
|
||||
## 🤝 Contributing
|
||||
|
||||
This is a personal lab setup, but feel free to fork and adapt for your needs!
|
||||
|
||||
## 📝 License
|
||||
|
||||
MIT License - Use at your own risk
|
||||
|
||||
## ⚠️ Disclaimer
|
||||
|
||||
This setup is designed for lab/self-hosting environments. For production use:
|
||||
- Use External Secrets for credentials
|
||||
- Set up proper TLS certificates
|
||||
- Configure backup automation
|
||||
- Enable monitoring and alerting
|
||||
- Review security best practices
|
||||
- Test email deliverability thoroughly
|
||||
298
QUICKSTART.md
Normal file
298
QUICKSTART.md
Normal file
@ -0,0 +1,298 @@
|
||||
# Quick Start Guide - Stalwart + SnappyMail Mail Stack
|
||||
|
||||
## 📋 What Was Created
|
||||
|
||||
A complete GitOps-ready mail stack with:
|
||||
- ✅ Stalwart Mail Server (all-in-one: SMTP, IMAP, Admin UI)
|
||||
- ✅ SnappyMail webmail client
|
||||
- ✅ Local Helm charts for both applications
|
||||
- ✅ ArgoCD Application manifests
|
||||
- ✅ Custom values files for dvirlabs.com
|
||||
- ✅ All manifests validated successfully
|
||||
|
||||
## 📁 File Structure Created
|
||||
|
||||
```
|
||||
mail-services/
|
||||
├── argocd-apps/
|
||||
│ ├── stalwart.yaml # ⚠️ UPDATE: Change repo URL
|
||||
│ └── snappymail.yaml # ⚠️ UPDATE: Change repo URL
|
||||
│
|
||||
├── charts/
|
||||
│ ├── stalwart/ # Local Helm chart for Stalwart
|
||||
│ │ ├── Chart.yaml
|
||||
│ │ ├── values.yaml
|
||||
│ │ └── templates/
|
||||
│ │ ├── _helpers.tpl
|
||||
│ │ ├── namespace.yaml
|
||||
│ │ ├── secret.yaml
|
||||
│ │ ├── statefulset.yaml
|
||||
│ │ ├── service.yaml
|
||||
│ │ └── ingress.yaml
|
||||
│ │
|
||||
│ └── snappymail/ # Local Helm chart for SnappyMail
|
||||
│ ├── Chart.yaml
|
||||
│ ├── values.yaml
|
||||
│ └── templates/
|
||||
│ ├── _helpers.tpl
|
||||
│ ├── deployment.yaml
|
||||
│ ├── pvc.yaml
|
||||
│ ├── service.yaml
|
||||
│ ├── ingress.yaml
|
||||
│ └── configmap.yaml
|
||||
│
|
||||
├── manifests/
|
||||
│ ├── stalwart/
|
||||
│ │ └── values.yaml # ⚠️ UPDATE: Change admin password
|
||||
│ └── snappymail/
|
||||
│ └── values.yaml
|
||||
│
|
||||
├── MAIL_STACK_README.md # 📖 Full documentation
|
||||
└── QUICKSTART.md # 👈 This file
|
||||
```
|
||||
|
||||
## ⚠️ REQUIRED CHANGES Before Deployment
|
||||
|
||||
### 1. Update Git Repository URL
|
||||
|
||||
Edit these files and replace `YOUR_USERNAME` with your actual Git username/organization:
|
||||
|
||||
**File: `argocd-apps/stalwart.yaml`**
|
||||
```yaml
|
||||
source:
|
||||
repoURL: https://github.com/YOUR_USERNAME/mail-services.git # ← CHANGE THIS
|
||||
```
|
||||
|
||||
**File: `argocd-apps/snappymail.yaml`**
|
||||
```yaml
|
||||
source:
|
||||
repoURL: https://github.com/YOUR_USERNAME/mail-services.git # ← CHANGE THIS
|
||||
```
|
||||
|
||||
### 2. Change Admin Password (CRITICAL!)
|
||||
|
||||
Edit `manifests/stalwart/values.yaml`:
|
||||
|
||||
Find this section:
|
||||
```yaml
|
||||
secret:
|
||||
create: true
|
||||
name: stalwart-credentials
|
||||
adminPassword: "CHANGE_ME_PLEASE_USE_STRONG_PASSWORD" # ← CHANGE THIS!
|
||||
```
|
||||
|
||||
Replace with a strong password:
|
||||
```yaml
|
||||
adminPassword: "MyStr0ng!P@ssw0rd#2024"
|
||||
```
|
||||
|
||||
**⚠️ DO NOT commit this file with the default password!**
|
||||
|
||||
### 3. (Optional) Update Domain Names
|
||||
|
||||
If you're not using `dvirlabs.com`, update these files:
|
||||
|
||||
**`manifests/stalwart/values.yaml`:**
|
||||
```yaml
|
||||
ingress:
|
||||
hosts:
|
||||
- host: mail.YOUR-DOMAIN.com # ← Update
|
||||
```
|
||||
|
||||
**`manifests/snappymail/values.yaml`:**
|
||||
```yaml
|
||||
ingress:
|
||||
hosts:
|
||||
- host: webmail.YOUR-DOMAIN.com # ← Update
|
||||
```
|
||||
|
||||
## 🚀 Deployment Steps
|
||||
|
||||
### Step 1: Commit and Push to Git
|
||||
|
||||
```bash
|
||||
cd c:\Users\dvirl\OneDrive\Desktop\gitea\mail-services
|
||||
|
||||
# Review changes
|
||||
git status
|
||||
|
||||
# Add new files
|
||||
git add argocd-apps/stalwart.yaml
|
||||
git add argocd-apps/snappymail.yaml
|
||||
git add charts/stalwart/
|
||||
git add charts/snappymail/
|
||||
git add manifests/stalwart/
|
||||
git add manifests/snappymail/
|
||||
git add MAIL_STACK_README.md
|
||||
git add QUICKSTART.md
|
||||
|
||||
# Commit
|
||||
git commit -m "Add Stalwart Mail Server + SnappyMail stack"
|
||||
|
||||
# Push to your Git server
|
||||
git push origin main
|
||||
```
|
||||
|
||||
### Step 2: Deploy with ArgoCD
|
||||
|
||||
```bash
|
||||
# Apply ArgoCD Applications
|
||||
kubectl apply -f argocd-apps/stalwart.yaml
|
||||
kubectl apply -f argocd-apps/snappymail.yaml
|
||||
|
||||
# Watch ArgoCD sync
|
||||
kubectl get applications -n argocd -w
|
||||
|
||||
# Watch pods come up
|
||||
kubectl get pods -n mail -w
|
||||
```
|
||||
|
||||
### Step 3: Verify Deployment
|
||||
|
||||
```bash
|
||||
# Check all resources
|
||||
kubectl get all -n mail
|
||||
|
||||
# Expected output:
|
||||
# - statefulset.apps/stalwart (1/1)
|
||||
# - deployment.apps/snappymail (1/1)
|
||||
# - service/stalwart
|
||||
# - service/snappymail
|
||||
# - ingress.networking.k8s.io/stalwart
|
||||
# - ingress.networking.k8s.io/snappymail
|
||||
|
||||
# Check PVCs
|
||||
kubectl get pvc -n mail
|
||||
|
||||
# Check logs
|
||||
kubectl logs -n mail stalwart-0
|
||||
kubectl logs -n mail -l app.kubernetes.io/name=snappymail
|
||||
```
|
||||
|
||||
## 🌐 Access the Services
|
||||
|
||||
### Stalwart Admin UI
|
||||
- URL: `https://mail.dvirlabs.com`
|
||||
- Username: `admin@dvirlabs.com`
|
||||
- Password: (what you set in manifests/stalwart/values.yaml)
|
||||
|
||||
### SnappyMail Webmail
|
||||
- URL: `https://webmail.dvirlabs.com`
|
||||
- First access: Admin panel at `https://webmail.dvirlabs.com/?admin`
|
||||
- Default admin password: `12345` (CHANGE IMMEDIATELY!)
|
||||
|
||||
## ⚙️ SnappyMail Configuration
|
||||
|
||||
After deployment, configure SnappyMail to connect to Stalwart:
|
||||
|
||||
1. Go to `https://webmail.dvirlabs.com/?admin`
|
||||
2. Login with default password `12345`
|
||||
3. Change admin password immediately
|
||||
4. Go to **Domains** → **Add Domain**
|
||||
5. Configure:
|
||||
- **IMAP Server:** `stalwart.mail.svc.cluster.local`
|
||||
- **IMAP Port:** `993`
|
||||
- **IMAP Secure:** `SSL/TLS`
|
||||
- **SMTP Server:** `stalwart.mail.svc.cluster.local`
|
||||
- **SMTP Port:** `587`
|
||||
- **SMTP Secure:** `STARTTLS`
|
||||
|
||||
## 📧 Setting Up Real Email
|
||||
|
||||
### DNS Records Needed
|
||||
|
||||
```dns
|
||||
; MX Record
|
||||
@ IN MX 10 mail.dvirlabs.com.
|
||||
|
||||
; A Record (use your public IP, NOT Cloudflare proxy)
|
||||
mail IN A YOUR_PUBLIC_IP
|
||||
|
||||
; SPF Record
|
||||
@ IN TXT "v=spf1 mx ~all"
|
||||
|
||||
; DMARC Record
|
||||
_dmarc IN TXT "v=DMARC1; p=quarantine; rua=mailto:admin@dvirlabs.com"
|
||||
```
|
||||
|
||||
### Port Forwarding Required
|
||||
|
||||
For real email (not just webmail), you need to expose these ports directly:
|
||||
|
||||
```
|
||||
Port 25 (SMTP) - Receiving mail
|
||||
Port 587 (SMTP) - Sending mail
|
||||
Port 993 (IMAPS) - IMAP access
|
||||
```
|
||||
|
||||
**⚠️ Important:** These ports CANNOT go through Cloudflare Tunnel!
|
||||
|
||||
## 🔍 Troubleshooting
|
||||
|
||||
### Pods stuck in Pending
|
||||
```bash
|
||||
# Check PVC status
|
||||
kubectl describe pvc -n mail
|
||||
|
||||
# Check if nfs-client storage class exists
|
||||
kubectl get storageclass
|
||||
```
|
||||
|
||||
### Can't access web UIs
|
||||
```bash
|
||||
# Check ingress
|
||||
kubectl describe ingress -n mail
|
||||
|
||||
# Check if DNS resolves to your cluster
|
||||
nslookup mail.dvirlabs.com
|
||||
nslookup webmail.dvirlabs.com
|
||||
```
|
||||
|
||||
### SnappyMail can't connect to Stalwart
|
||||
```bash
|
||||
# Test connectivity from SnappyMail pod
|
||||
kubectl exec -it -n mail deploy/snappymail -- nc -zv stalwart.mail.svc.cluster.local 993
|
||||
```
|
||||
|
||||
## 📖 Full Documentation
|
||||
|
||||
See [MAIL_STACK_README.md](MAIL_STACK_README.md) for:
|
||||
- Complete architecture overview
|
||||
- External mail setup instructions
|
||||
- Security hardening guide
|
||||
- Backup and restore procedures
|
||||
- Advanced configuration options
|
||||
- External Secrets integration
|
||||
|
||||
## ✅ Validation Results
|
||||
|
||||
All manifests have been validated:
|
||||
- ✅ Stalwart Helm chart renders correctly
|
||||
- ✅ SnappyMail Helm chart renders correctly
|
||||
- ✅ ArgoCD Application manifests are valid
|
||||
- ✅ All Kubernetes resources are syntactically correct
|
||||
|
||||
## 🎯 Next Steps
|
||||
|
||||
1. **Update repo URL** in ArgoCD manifests ← DO THIS FIRST!
|
||||
2. **Change admin password** in manifests/stalwart/values.yaml
|
||||
3. **Commit and push** to Git
|
||||
4. **Apply ArgoCD applications**
|
||||
5. **Wait for deployment** (2-3 minutes)
|
||||
6. **Access Stalwart admin UI** and configure mail settings
|
||||
7. **Configure SnappyMail** to connect to Stalwart
|
||||
8. **Set up DNS records** for real email
|
||||
9. **Configure port forwarding** for mail protocols
|
||||
|
||||
## 💡 Pro Tips
|
||||
|
||||
- Start with web UIs only, add real mail later
|
||||
- Use External Secrets for production passwords
|
||||
- Enable DKIM in Stalwart for better deliverability
|
||||
- Monitor logs during first email tests
|
||||
- Test with mail-tester.com for deliverability score
|
||||
- Backup mail data regularly
|
||||
|
||||
---
|
||||
|
||||
**Need help?** Check [MAIL_STACK_README.md](MAIL_STACK_README.md) for detailed documentation.
|
||||
36
argocd-apps/snappymail.yaml
Normal file
36
argocd-apps/snappymail.yaml
Normal file
@ -0,0 +1,36 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: snappymail
|
||||
namespace: argocd
|
||||
finalizers:
|
||||
- resources-finalizer.argocd.argoproj.io
|
||||
spec:
|
||||
project: default
|
||||
|
||||
source:
|
||||
repoURL: https://github.com/YOUR_USERNAME/mail-services.git # TODO: Update with your repo URL
|
||||
targetRevision: HEAD
|
||||
path: charts/snappymail
|
||||
helm:
|
||||
valueFiles:
|
||||
- ../../manifests/snappymail/values.yaml
|
||||
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: mail
|
||||
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: true
|
||||
selfHeal: true
|
||||
allowEmpty: false
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
- ServerSideApply=true
|
||||
retry:
|
||||
limit: 5
|
||||
backoff:
|
||||
duration: 5s
|
||||
factor: 2
|
||||
maxDuration: 3m
|
||||
36
argocd-apps/stalwart.yaml
Normal file
36
argocd-apps/stalwart.yaml
Normal file
@ -0,0 +1,36 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: stalwart
|
||||
namespace: argocd
|
||||
finalizers:
|
||||
- resources-finalizer.argocd.argoproj.io
|
||||
spec:
|
||||
project: default
|
||||
|
||||
source:
|
||||
repoURL: https://github.com/YOUR_USERNAME/mail-services.git # TODO: Update with your repo URL
|
||||
targetRevision: HEAD
|
||||
path: charts/stalwart
|
||||
helm:
|
||||
valueFiles:
|
||||
- ../../manifests/stalwart/values.yaml
|
||||
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: mail
|
||||
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: true
|
||||
selfHeal: true
|
||||
allowEmpty: false
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
- ServerSideApply=true
|
||||
retry:
|
||||
limit: 5
|
||||
backoff:
|
||||
duration: 5s
|
||||
factor: 2
|
||||
maxDuration: 3m
|
||||
15
charts/snappymail/Chart.yaml
Normal file
15
charts/snappymail/Chart.yaml
Normal file
@ -0,0 +1,15 @@
|
||||
apiVersion: v2
|
||||
name: snappymail
|
||||
description: SnappyMail - Simple, modern, lightweight webmail client
|
||||
type: application
|
||||
version: 1.0.0
|
||||
appVersion: "2.39.1"
|
||||
keywords:
|
||||
- webmail
|
||||
- mail
|
||||
- snappymail
|
||||
home: https://snappymail.eu
|
||||
sources:
|
||||
- https://github.com/the-djmaze/snappymail
|
||||
maintainers:
|
||||
- name: dvirlabs
|
||||
49
charts/snappymail/templates/_helpers.tpl
Normal file
49
charts/snappymail/templates/_helpers.tpl
Normal file
@ -0,0 +1,49 @@
|
||||
{{/*
|
||||
Expand the name of the chart.
|
||||
*/}}
|
||||
{{- define "snappymail.name" -}}
|
||||
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create a default fully qualified app name.
|
||||
*/}}
|
||||
{{- define "snappymail.fullname" -}}
|
||||
{{- if .Values.fullnameOverride }}
|
||||
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- else }}
|
||||
{{- $name := default .Chart.Name .Values.nameOverride }}
|
||||
{{- if contains $name .Release.Name }}
|
||||
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
|
||||
{{- else }}
|
||||
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create chart name and version as used by the chart label.
|
||||
*/}}
|
||||
{{- define "snappymail.chart" -}}
|
||||
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Common labels
|
||||
*/}}
|
||||
{{- define "snappymail.labels" -}}
|
||||
helm.sh/chart: {{ include "snappymail.chart" . }}
|
||||
{{ include "snappymail.selectorLabels" . }}
|
||||
{{- if .Chart.AppVersion }}
|
||||
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
|
||||
{{- end }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Selector labels
|
||||
*/}}
|
||||
{{- define "snappymail.selectorLabels" -}}
|
||||
app.kubernetes.io/name: {{ include "snappymail.name" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
{{- end }}
|
||||
15
charts/snappymail/templates/configmap.yaml
Normal file
15
charts/snappymail/templates/configmap.yaml
Normal file
@ -0,0 +1,15 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: {{ include "snappymail.fullname" . }}-config
|
||||
namespace: {{ .Values.namespace }}
|
||||
labels:
|
||||
{{- include "snappymail.labels" . | nindent 4 }}
|
||||
data:
|
||||
# Stalwart mail server connection info
|
||||
# This can be used for documentation or manual configuration
|
||||
# SnappyMail is configured through its web UI
|
||||
IMAP_HOST: {{ .Values.stalwart.imap.host | quote }}
|
||||
IMAP_PORT: {{ .Values.stalwart.imap.port | quote }}
|
||||
SMTP_HOST: {{ .Values.stalwart.smtp.host | quote }}
|
||||
SMTP_PORT: {{ .Values.stalwart.smtp.port | quote }}
|
||||
69
charts/snappymail/templates/deployment.yaml
Normal file
69
charts/snappymail/templates/deployment.yaml
Normal file
@ -0,0 +1,69 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ include "snappymail.fullname" . }}
|
||||
namespace: {{ .Values.namespace }}
|
||||
labels:
|
||||
{{- include "snappymail.labels" . | nindent 4 }}
|
||||
spec:
|
||||
replicas: {{ .Values.replicaCount }}
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "snappymail.selectorLabels" . | nindent 6 }}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
{{- include "snappymail.selectorLabels" . | nindent 8 }}
|
||||
spec:
|
||||
securityContext:
|
||||
{{- toYaml .Values.securityContext | nindent 8 }}
|
||||
containers:
|
||||
- name: snappymail
|
||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: {{ .Values.service.targetPort }}
|
||||
protocol: TCP
|
||||
volumeMounts:
|
||||
- name: data
|
||||
mountPath: {{ .Values.persistence.mountPath }}
|
||||
resources:
|
||||
{{- toYaml .Values.resources | nindent 10 }}
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: http
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 10
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: http
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 5
|
||||
{{- with .Values.env }}
|
||||
env:
|
||||
{{- toYaml . | nindent 10 }}
|
||||
{{- end }}
|
||||
volumes:
|
||||
{{- if .Values.persistence.enabled }}
|
||||
- name: data
|
||||
persistentVolumeClaim:
|
||||
claimName: {{ include "snappymail.fullname" . }}
|
||||
{{- else }}
|
||||
- name: data
|
||||
emptyDir: {}
|
||||
{{- end }}
|
||||
{{- with .Values.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.affinity }}
|
||||
affinity:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.tolerations }}
|
||||
tolerations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
42
charts/snappymail/templates/ingress.yaml
Normal file
42
charts/snappymail/templates/ingress.yaml
Normal file
@ -0,0 +1,42 @@
|
||||
{{- if .Values.ingress.enabled -}}
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: {{ include "snappymail.fullname" . }}
|
||||
namespace: {{ .Values.namespace }}
|
||||
labels:
|
||||
{{- include "snappymail.labels" . | nindent 4 }}
|
||||
{{- with .Values.ingress.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- if .Values.ingress.className }}
|
||||
ingressClassName: {{ .Values.ingress.className }}
|
||||
{{- end }}
|
||||
{{- if .Values.ingress.tls }}
|
||||
tls:
|
||||
{{- range .Values.ingress.tls }}
|
||||
- hosts:
|
||||
{{- range .hosts }}
|
||||
- {{ . | quote }}
|
||||
{{- end }}
|
||||
secretName: {{ .secretName }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
rules:
|
||||
{{- range .Values.ingress.hosts }}
|
||||
- host: {{ .host | quote }}
|
||||
http:
|
||||
paths:
|
||||
{{- range .paths }}
|
||||
- path: {{ .path }}
|
||||
pathType: {{ .pathType }}
|
||||
backend:
|
||||
service:
|
||||
name: {{ include "snappymail.fullname" $ }}
|
||||
port:
|
||||
number: {{ $.Values.service.port }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
16
charts/snappymail/templates/pvc.yaml
Normal file
16
charts/snappymail/templates/pvc.yaml
Normal file
@ -0,0 +1,16 @@
|
||||
{{- if .Values.persistence.enabled }}
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: {{ include "snappymail.fullname" . }}
|
||||
namespace: {{ .Values.namespace }}
|
||||
labels:
|
||||
{{- include "snappymail.labels" . | nindent 4 }}
|
||||
spec:
|
||||
accessModes:
|
||||
- {{ .Values.persistence.accessMode }}
|
||||
storageClassName: {{ .Values.persistence.storageClass }}
|
||||
resources:
|
||||
requests:
|
||||
storage: {{ .Values.persistence.size }}
|
||||
{{- end }}
|
||||
16
charts/snappymail/templates/service.yaml
Normal file
16
charts/snappymail/templates/service.yaml
Normal file
@ -0,0 +1,16 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ include "snappymail.fullname" . }}
|
||||
namespace: {{ .Values.namespace }}
|
||||
labels:
|
||||
{{- include "snappymail.labels" . | nindent 4 }}
|
||||
spec:
|
||||
type: {{ .Values.service.type }}
|
||||
ports:
|
||||
- port: {{ .Values.service.port }}
|
||||
targetPort: {{ .Values.service.targetPort }}
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
{{- include "snappymail.selectorLabels" . | nindent 4 }}
|
||||
86
charts/snappymail/values.yaml
Normal file
86
charts/snappymail/values.yaml
Normal file
@ -0,0 +1,86 @@
|
||||
## SnappyMail webmail configuration
|
||||
## Default values for snappymail chart
|
||||
|
||||
## Image configuration
|
||||
image:
|
||||
repository: djmaze/snappymail
|
||||
tag: "2.39"
|
||||
pullPolicy: IfNotPresent
|
||||
|
||||
## Namespace
|
||||
namespace: mail
|
||||
|
||||
## Replica count
|
||||
replicaCount: 1
|
||||
|
||||
## Service configuration
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 8888
|
||||
targetPort: 8888
|
||||
|
||||
## Ingress configuration
|
||||
ingress:
|
||||
enabled: true
|
||||
className: traefik
|
||||
annotations:
|
||||
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
||||
traefik.ingress.kubernetes.io/router.tls: "true"
|
||||
hosts:
|
||||
- host: webmail.dvirlabs.com
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
tls: []
|
||||
|
||||
## Persistence configuration for SnappyMail data
|
||||
persistence:
|
||||
enabled: true
|
||||
storageClass: nfs-client
|
||||
accessMode: ReadWriteOnce
|
||||
size: 1Gi
|
||||
# Where SnappyMail stores its data
|
||||
mountPath: /var/lib/snappymail
|
||||
|
||||
## Resource limits
|
||||
resources:
|
||||
requests:
|
||||
memory: "128Mi"
|
||||
cpu: "100m"
|
||||
limits:
|
||||
memory: "512Mi"
|
||||
cpu: "500m"
|
||||
|
||||
## Stalwart mail server configuration
|
||||
## SnappyMail will connect to Stalwart internally
|
||||
stalwart:
|
||||
# Service name of Stalwart in the same namespace
|
||||
serviceName: stalwart
|
||||
# IMAP configuration
|
||||
imap:
|
||||
host: stalwart.mail.svc.cluster.local
|
||||
port: 993
|
||||
secure: true
|
||||
# SMTP configuration
|
||||
smtp:
|
||||
host: stalwart.mail.svc.cluster.local
|
||||
port: 587
|
||||
secure: true
|
||||
|
||||
## Environment variables
|
||||
env: {}
|
||||
|
||||
## Pod Security Context
|
||||
securityContext:
|
||||
fsGroup: 82 # www-data group in Alpine
|
||||
runAsUser: 82
|
||||
runAsNonRoot: true
|
||||
|
||||
## Node selector
|
||||
nodeSelector: {}
|
||||
|
||||
## Tolerations
|
||||
tolerations: []
|
||||
|
||||
## Affinity
|
||||
affinity: {}
|
||||
16
charts/stalwart/Chart.yaml
Normal file
16
charts/stalwart/Chart.yaml
Normal file
@ -0,0 +1,16 @@
|
||||
apiVersion: v2
|
||||
name: stalwart
|
||||
description: Stalwart Mail Server - Modern all-in-one mail server
|
||||
type: application
|
||||
version: 1.0.0
|
||||
appVersion: "0.10.8"
|
||||
keywords:
|
||||
- mail
|
||||
- smtp
|
||||
- imap
|
||||
- stalwart
|
||||
home: https://stalw.art
|
||||
sources:
|
||||
- https://github.com/stalwartlabs/mail-server
|
||||
maintainers:
|
||||
- name: dvirlabs
|
||||
60
charts/stalwart/templates/_helpers.tpl
Normal file
60
charts/stalwart/templates/_helpers.tpl
Normal file
@ -0,0 +1,60 @@
|
||||
{{/*
|
||||
Expand the name of the chart.
|
||||
*/}}
|
||||
{{- define "stalwart.name" -}}
|
||||
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create a default fully qualified app name.
|
||||
*/}}
|
||||
{{- define "stalwart.fullname" -}}
|
||||
{{- if .Values.fullnameOverride }}
|
||||
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- else }}
|
||||
{{- $name := default .Chart.Name .Values.nameOverride }}
|
||||
{{- if contains $name .Release.Name }}
|
||||
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
|
||||
{{- else }}
|
||||
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create chart name and version as used by the chart label.
|
||||
*/}}
|
||||
{{- define "stalwart.chart" -}}
|
||||
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Common labels
|
||||
*/}}
|
||||
{{- define "stalwart.labels" -}}
|
||||
helm.sh/chart: {{ include "stalwart.chart" . }}
|
||||
{{ include "stalwart.selectorLabels" . }}
|
||||
{{- if .Chart.AppVersion }}
|
||||
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
|
||||
{{- end }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Selector labels
|
||||
*/}}
|
||||
{{- define "stalwart.selectorLabels" -}}
|
||||
app.kubernetes.io/name: {{ include "stalwart.name" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create the name of the service account to use
|
||||
*/}}
|
||||
{{- define "stalwart.serviceAccountName" -}}
|
||||
{{- if .Values.serviceAccount.create }}
|
||||
{{- default (include "stalwart.fullname" .) .Values.serviceAccount.name }}
|
||||
{{- else }}
|
||||
{{- default "default" .Values.serviceAccount.name }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
42
charts/stalwart/templates/ingress.yaml
Normal file
42
charts/stalwart/templates/ingress.yaml
Normal file
@ -0,0 +1,42 @@
|
||||
{{- if .Values.ingress.enabled -}}
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: {{ include "stalwart.fullname" . }}
|
||||
namespace: {{ .Values.namespace }}
|
||||
labels:
|
||||
{{- include "stalwart.labels" . | nindent 4 }}
|
||||
{{- with .Values.ingress.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- if .Values.ingress.className }}
|
||||
ingressClassName: {{ .Values.ingress.className }}
|
||||
{{- end }}
|
||||
{{- if .Values.ingress.tls }}
|
||||
tls:
|
||||
{{- range .Values.ingress.tls }}
|
||||
- hosts:
|
||||
{{- range .hosts }}
|
||||
- {{ . | quote }}
|
||||
{{- end }}
|
||||
secretName: {{ .secretName }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
rules:
|
||||
{{- range .Values.ingress.hosts }}
|
||||
- host: {{ .host | quote }}
|
||||
http:
|
||||
paths:
|
||||
{{- range .paths }}
|
||||
- path: {{ .path }}
|
||||
pathType: {{ .pathType }}
|
||||
backend:
|
||||
service:
|
||||
name: {{ include "stalwart.fullname" $ }}
|
||||
port:
|
||||
number: {{ .servicePort }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
6
charts/stalwart/templates/namespace.yaml
Normal file
6
charts/stalwart/templates/namespace.yaml
Normal file
@ -0,0 +1,6 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: {{ .Values.namespace }}
|
||||
labels:
|
||||
{{- include "stalwart.labels" . | nindent 4 }}
|
||||
12
charts/stalwart/templates/secret.yaml
Normal file
12
charts/stalwart/templates/secret.yaml
Normal file
@ -0,0 +1,12 @@
|
||||
{{- if .Values.secret.create }}
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: {{ .Values.secret.name }}
|
||||
namespace: {{ .Values.namespace }}
|
||||
labels:
|
||||
{{- include "stalwart.labels" . | nindent 4 }}
|
||||
type: Opaque
|
||||
stringData:
|
||||
STALWART_ADMIN_PASSWORD: {{ .Values.secret.adminPassword | quote }}
|
||||
{{- end }}
|
||||
36
charts/stalwart/templates/service.yaml
Normal file
36
charts/stalwart/templates/service.yaml
Normal file
@ -0,0 +1,36 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ include "stalwart.fullname" . }}
|
||||
namespace: {{ .Values.namespace }}
|
||||
labels:
|
||||
{{- include "stalwart.labels" . | nindent 4 }}
|
||||
spec:
|
||||
type: {{ .Values.service.type }}
|
||||
ports:
|
||||
- port: {{ .Values.service.web.port }}
|
||||
targetPort: {{ .Values.service.web.targetPort }}
|
||||
protocol: TCP
|
||||
name: web
|
||||
- port: {{ .Values.service.smtp.port }}
|
||||
targetPort: {{ .Values.service.smtp.targetPort }}
|
||||
protocol: TCP
|
||||
name: smtp
|
||||
- port: {{ .Values.service.smtps.port }}
|
||||
targetPort: {{ .Values.service.smtps.targetPort }}
|
||||
protocol: TCP
|
||||
name: smtps
|
||||
- port: {{ .Values.service.submission.port }}
|
||||
targetPort: {{ .Values.service.submission.targetPort }}
|
||||
protocol: TCP
|
||||
name: submission
|
||||
- port: {{ .Values.service.imap.port }}
|
||||
targetPort: {{ .Values.service.imap.targetPort }}
|
||||
protocol: TCP
|
||||
name: imap
|
||||
- port: {{ .Values.service.imaps.port }}
|
||||
targetPort: {{ .Values.service.imaps.targetPort }}
|
||||
protocol: TCP
|
||||
name: imaps
|
||||
selector:
|
||||
{{- include "stalwart.selectorLabels" . | nindent 4 }}
|
||||
94
charts/stalwart/templates/statefulset.yaml
Normal file
94
charts/stalwart/templates/statefulset.yaml
Normal file
@ -0,0 +1,94 @@
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: {{ include "stalwart.fullname" . }}
|
||||
namespace: {{ .Values.namespace }}
|
||||
labels:
|
||||
{{- include "stalwart.labels" . | nindent 4 }}
|
||||
spec:
|
||||
serviceName: {{ include "stalwart.fullname" . }}
|
||||
replicas: {{ .Values.replicaCount }}
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "stalwart.selectorLabels" . | nindent 6 }}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
{{- include "stalwart.selectorLabels" . | nindent 8 }}
|
||||
spec:
|
||||
securityContext:
|
||||
{{- toYaml .Values.securityContext | nindent 8 }}
|
||||
containers:
|
||||
- name: stalwart
|
||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
ports:
|
||||
- name: web
|
||||
containerPort: {{ .Values.service.web.targetPort }}
|
||||
protocol: TCP
|
||||
- name: smtp
|
||||
containerPort: {{ .Values.service.smtp.targetPort }}
|
||||
protocol: TCP
|
||||
- name: smtps
|
||||
containerPort: {{ .Values.service.smtps.targetPort }}
|
||||
protocol: TCP
|
||||
- name: submission
|
||||
containerPort: {{ .Values.service.submission.targetPort }}
|
||||
protocol: TCP
|
||||
- name: imap
|
||||
containerPort: {{ .Values.service.imap.targetPort }}
|
||||
protocol: TCP
|
||||
- name: imaps
|
||||
containerPort: {{ .Values.service.imaps.targetPort }}
|
||||
protocol: TCP
|
||||
env:
|
||||
- name: STALWART_ADMIN_USER
|
||||
value: {{ .Values.env.STALWART_ADMIN_USER | quote }}
|
||||
- name: STALWART_ADMIN_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ .Values.secret.name }}
|
||||
key: STALWART_ADMIN_PASSWORD
|
||||
volumeMounts:
|
||||
- name: data
|
||||
mountPath: {{ .Values.persistence.mountPath }}
|
||||
resources:
|
||||
{{- toYaml .Values.resources | nindent 10 }}
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: web
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 10
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: web
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 5
|
||||
{{- with .Values.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.affinity }}
|
||||
affinity:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.tolerations }}
|
||||
tolerations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if .Values.persistence.enabled }}
|
||||
volumeClaimTemplates:
|
||||
- metadata:
|
||||
name: data
|
||||
labels:
|
||||
{{- include "stalwart.labels" . | nindent 8 }}
|
||||
spec:
|
||||
accessModes:
|
||||
- {{ .Values.persistence.accessMode }}
|
||||
storageClassName: {{ .Values.persistence.storageClass }}
|
||||
resources:
|
||||
requests:
|
||||
storage: {{ .Values.persistence.size }}
|
||||
{{- end }}
|
||||
104
charts/stalwart/values.yaml
Normal file
104
charts/stalwart/values.yaml
Normal file
@ -0,0 +1,104 @@
|
||||
## Stalwart Mail Server configuration
|
||||
## Default values for stalwart chart
|
||||
|
||||
## Image configuration
|
||||
image:
|
||||
repository: stalwartlabs/mail-server
|
||||
tag: v0.10.8
|
||||
pullPolicy: IfNotPresent
|
||||
|
||||
## Namespace
|
||||
namespace: mail
|
||||
|
||||
## Replica count (should be 1 for StatefulSet with persistent data)
|
||||
replicaCount: 1
|
||||
|
||||
## Service configuration
|
||||
service:
|
||||
type: ClusterIP
|
||||
# Admin/Web UI port
|
||||
web:
|
||||
port: 8080
|
||||
targetPort: 8080
|
||||
# SMTP ports
|
||||
smtp:
|
||||
port: 25
|
||||
targetPort: 25
|
||||
smtps:
|
||||
port: 465
|
||||
targetPort: 465
|
||||
submission:
|
||||
port: 587
|
||||
targetPort: 587
|
||||
# IMAP ports
|
||||
imap:
|
||||
port: 143
|
||||
targetPort: 143
|
||||
imaps:
|
||||
port: 993
|
||||
targetPort: 993
|
||||
|
||||
## Ingress configuration for admin UI
|
||||
ingress:
|
||||
enabled: true
|
||||
className: traefik
|
||||
annotations:
|
||||
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
||||
traefik.ingress.kubernetes.io/router.tls: "true"
|
||||
hosts:
|
||||
- host: mail.dvirlabs.com
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
servicePort: 8080
|
||||
tls: []
|
||||
|
||||
## Persistence configuration
|
||||
persistence:
|
||||
enabled: true
|
||||
storageClass: nfs-client
|
||||
accessMode: ReadWriteOnce
|
||||
size: 10Gi
|
||||
# Where Stalwart stores mail data
|
||||
mountPath: /opt/stalwart-mail
|
||||
|
||||
## Resource limits
|
||||
resources:
|
||||
requests:
|
||||
memory: "512Mi"
|
||||
cpu: "250m"
|
||||
limits:
|
||||
memory: "2Gi"
|
||||
cpu: "1000m"
|
||||
|
||||
## Environment variables for Stalwart configuration
|
||||
env:
|
||||
# Admin credentials - CHANGE THESE!
|
||||
# Or reference a secret using envFrom
|
||||
STALWART_ADMIN_USER: "admin@dvirlabs.com"
|
||||
# Password should come from a secret
|
||||
# STALWART_ADMIN_PASSWORD: defined in secret
|
||||
|
||||
## Secret configuration
|
||||
secret:
|
||||
# Set to true to create a secret
|
||||
create: true
|
||||
# Name of the secret (if create: false, this should reference an existing secret)
|
||||
name: stalwart-credentials
|
||||
# Admin password - CHANGE THIS or use external secrets later
|
||||
adminPassword: "CHANGE_ME_PLEASE"
|
||||
|
||||
## Pod Security Context
|
||||
securityContext:
|
||||
fsGroup: 1000
|
||||
runAsUser: 1000
|
||||
runAsNonRoot: true
|
||||
|
||||
## Node selector
|
||||
nodeSelector: {}
|
||||
|
||||
## Tolerations
|
||||
tolerations: []
|
||||
|
||||
## Affinity
|
||||
affinity: {}
|
||||
54
manifests/snappymail/values.yaml
Normal file
54
manifests/snappymail/values.yaml
Normal file
@ -0,0 +1,54 @@
|
||||
## SnappyMail - Custom values for dvirlabs.com
|
||||
## Override default chart values here
|
||||
|
||||
## Namespace
|
||||
namespace: mail
|
||||
|
||||
## Image configuration
|
||||
image:
|
||||
repository: djmaze/snappymail
|
||||
tag: "2.39"
|
||||
pullPolicy: IfNotPresent
|
||||
|
||||
## Persistence - using NFS storage
|
||||
persistence:
|
||||
enabled: true
|
||||
storageClass: nfs-client
|
||||
accessMode: ReadWriteOnce
|
||||
size: 2Gi
|
||||
mountPath: /var/lib/snappymail
|
||||
|
||||
## Ingress for webmail UI
|
||||
ingress:
|
||||
enabled: true
|
||||
className: traefik
|
||||
annotations:
|
||||
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
||||
traefik.ingress.kubernetes.io/router.tls: "true"
|
||||
hosts:
|
||||
- host: webmail.dvirlabs.com
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
|
||||
## Stalwart mail server connection
|
||||
## SnappyMail will connect to Stalwart internally
|
||||
stalwart:
|
||||
serviceName: stalwart
|
||||
imap:
|
||||
host: stalwart.mail.svc.cluster.local
|
||||
port: 993
|
||||
secure: true
|
||||
smtp:
|
||||
host: stalwart.mail.svc.cluster.local
|
||||
port: 587
|
||||
secure: true
|
||||
|
||||
## Resource allocation
|
||||
resources:
|
||||
requests:
|
||||
memory: "256Mi"
|
||||
cpu: "200m"
|
||||
limits:
|
||||
memory: "1Gi"
|
||||
cpu: "1000m"
|
||||
54
manifests/stalwart/values.yaml
Normal file
54
manifests/stalwart/values.yaml
Normal file
@ -0,0 +1,54 @@
|
||||
## Stalwart Mail Server - Custom values for dvirlabs.com
|
||||
## Override default chart values here
|
||||
|
||||
## Namespace
|
||||
namespace: mail
|
||||
|
||||
## Image configuration
|
||||
image:
|
||||
repository: stalwartlabs/mail-server
|
||||
tag: v0.10.8
|
||||
pullPolicy: IfNotPresent
|
||||
|
||||
## Persistence - using NFS storage
|
||||
persistence:
|
||||
enabled: true
|
||||
storageClass: nfs-client
|
||||
accessMode: ReadWriteOnce
|
||||
size: 20Gi # Increased for production mail storage
|
||||
mountPath: /opt/stalwart-mail
|
||||
|
||||
## Ingress for admin UI only
|
||||
ingress:
|
||||
enabled: true
|
||||
className: traefik
|
||||
annotations:
|
||||
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
||||
traefik.ingress.kubernetes.io/router.tls: "true"
|
||||
hosts:
|
||||
- host: mail.dvirlabs.com
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
servicePort: 8080
|
||||
|
||||
## Admin credentials
|
||||
env:
|
||||
STALWART_ADMIN_USER: "admin@dvirlabs.com"
|
||||
|
||||
## Secret configuration
|
||||
## IMPORTANT: Change the adminPassword before deploying!
|
||||
secret:
|
||||
create: true
|
||||
name: stalwart-credentials
|
||||
# TODO: Replace with a strong password or use External Secrets
|
||||
adminPassword: "CHANGE_ME_PLEASE_USE_STRONG_PASSWORD"
|
||||
|
||||
## Resource allocation
|
||||
resources:
|
||||
requests:
|
||||
memory: "1Gi"
|
||||
cpu: "500m"
|
||||
limits:
|
||||
memory: "4Gi"
|
||||
cpu: "2000m"
|
||||
Loading…
x
Reference in New Issue
Block a user