Email Worker
The Email Worker is a production-ready worker that sends emails via SMTP. It supports multiple SMTP configurations, HTML and plain text emails, attachments, CC/BCC, and all standard email features.
Features
- Multiple SMTP configurations (Default, Marketing, Transactional, etc.)
- HTML and plain text email body
- File attachments (regular and inline for HTML)
- CC and BCC recipients
- Custom email headers
- Email priority levels (High, Normal, Low)
- Read and delivery receipts
- Reply-To address
- Automatic retry for transient errors
- Permanent failure detection (no retry for invalid recipients)
Use Cases
| Scenario | Example |
|---|---|
| Transactional Emails | Order confirmations, password resets |
| Notifications | Alert emails, status updates |
| Marketing Campaigns | Scheduled newsletter sends |
| Reports | Daily/weekly report delivery with attachments |
| System Alerts | Error notifications, monitoring alerts |
| Welcome Emails | New user onboarding sequences |
Security Model
Like the SQL Worker, SMTP credentials are stored securely in the worker configuration, not in job data.
Worker (appsettings.json)
EmailConfig
└─ SmtpConfigs
└─ Default
├─ Host: smtp.gmail.com
├─ Username: ********
└─ Password: ********
(secrets stay here)
Only the configuration name (alias) is referenced in job data:
{
"configName": "Default",
"to": ["[email protected]"],
"subject": "Welcome!",
"body": "<h1>Hello!</h1>"
}
This design ensures that sensitive credentials never leave the worker environment.
Worker Configuration
Configure SMTP servers in the worker's appsettings.json:
{
"EmailConfig": {
"DefaultConfigName": "Default",
"SmtpConfigs": {
"Default": {
"Host": "smtp.gmail.com",
"Port": 587,
"Username": "[email protected]",
"Password": "your-app-password",
"UseSsl": true,
"DefaultFromEmail": "[email protected]",
"DefaultFromName": "Your Company",
"TimeoutSeconds": 30,
"IgnoreCertificateErrors": false
},
"Marketing": {
"Host": "smtp.sendgrid.net",
"Port": 587,
"Username": "apikey",
"Password": "your-sendgrid-api-key",
"UseSsl": true,
"DefaultFromEmail": "[email protected]",
"DefaultFromName": "Your Company Marketing",
"TimeoutSeconds": 30
},
"Transactional": {
"Host": "smtp.mailgun.org",
"Port": 587,
"Username": "[email protected]",
"Password": "your-mailgun-password",
"UseSsl": true,
"DefaultFromEmail": "[email protected]",
"DefaultFromName": "Your Company"
}
}
}
}
SMTP Configuration Properties
| Property | Type | Required | Default | Description |
|---|---|---|---|---|
Host | string | ✓ | - | SMTP server hostname |
Port | number | - | 587 | SMTP port (25, 465, or 587) |
Username | string | - | - | SMTP authentication username |
Password | string | - | - | SMTP authentication password |
UseSsl | boolean | - | true | Enable SSL/TLS encryption |
DefaultFromEmail | string | ✓ | - | Default sender email address |
DefaultFromName | string | - | - | Default sender display name |
TimeoutSeconds | number | - | 30 | Connection timeout |
IgnoreCertificateErrors | boolean | - | false | Skip SSL certificate validation |
Job Data Schema
When creating an email job, provide the email details through Job Data JSON:
{
"configName": "Default",
"to": ["[email protected]"],
"cc": ["[email protected]"],
"bcc": ["[email protected]"],
"subject": "Monthly Report - January 2024",
"body": "<h1>Monthly Report</h1><p>Please find attached...</p>",
"isHtml": true,
"fromEmail": "[email protected]",
"fromName": "Reports System",
"replyTo": "[email protected]",
"priority": "High",
"attachments": [
{
"fileName": "report.pdf",
"contentBase64": "JVBERi0xLjQK...",
"contentType": "application/pdf"
}
],
"requestReadReceipt": false,
"requestDeliveryReceipt": true
}
Configuration Reference
Main Properties
| Property | Type | Required | Default | Description |
|---|---|---|---|---|
configName | string | - | Default config | SMTP configuration alias |
to | array | ✓ | - | Recipient email addresses |
cc | array | - | - | CC recipients |
bcc | array | - | - | BCC recipients (hidden) |
subject | string | ✓ | - | Email subject line |
body | string | ✓ | - | Email body content |
isHtml | boolean | - | false | Whether body is HTML |
fromEmail | string | - | From config | Override sender email |
fromName | string | - | From config | Override sender name |
replyTo | string | - | - | Reply-To address |
priority | enum | - | Normal | Priority: Low, Normal, High |
attachments | array | - | - | File attachments |
customHeaders | object | - | - | Custom email headers |
requestReadReceipt | boolean | - | false | Request read receipt |
requestDeliveryReceipt | boolean | - | false | Request delivery receipt |
Attachment Properties
| Property | Type | Required | Description |
|---|---|---|---|
fileName | string | ✓ | File name with extension |
contentBase64 | string | ✓ | Base64-encoded file content |
contentType | string | - | MIME type (auto-detected if omitted) |
isInline | boolean | - | Inline attachment for HTML |
contentId | string | - | Content ID for inline (use as cid:value) |
Deployment
docker run -d --name milvaion-email-worker --network milvaion_milvaion-network milvasoft/milvaion-email-worker:latest
Docker Compose
services:
email-worker:
image: milvasoft/milvaion-email-worker:latest
environment:
- Worker__WorkerId=email-worker-01
- Worker__RabbitMQ__Host=rabbitmq
- Worker__Redis__ConnectionString=redis:6379
- EmailConfig__DefaultConfigName=Default
- EmailConfig__SmtpConfigs__Default__Host=smtp.gmail.com
- EmailConfig__SmtpConfigs__Default__Port=587
- EmailConfig__SmtpConfigs__Default__Username=${SMTP_USERNAME}
- EmailConfig__SmtpConfigs__Default__Password=${SMTP_PASSWORD}
- EmailConfig__SmtpConfigs__Default__UseSsl=true
- EmailConfig__SmtpConfigs__Default__DefaultFromEmail=noreply@yourcompany.com
- EmailConfig__SmtpConfigs__Default__DefaultFromName=Your Company
depends_on:
- rabbitmq
- redis
restart: unless-stopped
Environment Variables
All SMTP configurations can be overridden via environment variables:
# Default SMTP Configuration
EmailConfig__SmtpConfigs__Default__Host=smtp.gmail.com
EmailConfig__SmtpConfigs__Default__Port=587
EmailConfig__SmtpConfigs__Default__Username=[email protected]
EmailConfig__SmtpConfigs__Default__Password=your-app-password
EmailConfig__SmtpConfigs__Default__UseSsl=true
EmailConfig__SmtpConfigs__Default__DefaultFromEmail=[email protected]
EmailConfig__SmtpConfigs__Default__DefaultFromName=Your Company
# Marketing SMTP Configuration
EmailConfig__SmtpConfigs__Marketing__Host=smtp.sendgrid.net
EmailConfig__SmtpConfigs__Marketing__Port=587
EmailConfig__SmtpConfigs__Marketing__Username=apikey
EmailConfig__SmtpConfigs__Marketing__Password=your-sendgrid-api-key
# Timeout and SSL Settings
EmailConfig__SmtpConfigs__Default__TimeoutSeconds=30
EmailConfig__SmtpConfigs__Default__IgnoreCertificateErrors=false
Kubernetes
apiVersion: apps/v1
kind: Deployment
metadata:
name: email-worker
spec:
replicas: 2
selector:
matchLabels:
app: email-worker
template:
metadata:
labels:
app: email-worker
spec:
containers:
- name: email-worker
image: milvasoft/milvaion-email-worker:latest
env:
- name: Worker__WorkerId
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: Worker__RabbitMQ__Host
value: rabbitmq-service
- name: Worker__Redis__ConnectionString
value: redis-service:6379
- name: EmailConfig__SmtpConfigs__Default__Password
valueFrom:
secretKeyRef:
name: email-secrets
key: smtp-password
envFrom:
- configMapRef:
name: email-worker-config
---
apiVersion: v1
kind: Secret
metadata:
name: email-secrets
type: Opaque
stringData:
smtp-password: your-smtp-password
---
apiVersion: v1
kind: ConfigMap
metadata:
name: email-worker-config
data:
EmailConfig__SmtpConfigs__Default__Host: smtp.gmail.com
EmailConfig__SmtpConfigs__Default__Port: "587"
EmailConfig__SmtpConfigs__Default__Username: your-[email protected]
EmailConfig__SmtpConfigs__Default__UseSsl: "true"
EmailConfig__SmtpConfigs__Default__DefaultFromEmail: [email protected]
EmailConfig__SmtpConfigs__Default__DefaultFromName: Your Company
Scaling Considerations
| Factor | Recommendation |
|---|---|
| Replicas | 2-4 workers for high-volume email sending |
| Rate Limiting | Respect SMTP provider limits (Gmail: 500/day, SendGrid: varies) |
| Retry Strategy | Built-in retry handles transient failures automatically |
| Memory | ~256MB per worker (more if handling large attachments) |
For custom workers, see Your First Worker and Implementing Jobs.