Automated AWS infrastructure deployment for the Mail Server using ts-cloud with TypeScript and Bun.
- Overview
- Architecture
- Prerequisites
- Quick Start
- Configuration
- Deployment
- Monitoring
- Troubleshooting
- Cost Estimation
- Security
This ts-cloud application deploys a complete Mail Server infrastructure on AWS, including:
- ✅ EC2 instance with optimized configuration
- ✅ VPC with public subnet
- ✅ Security groups for all mail protocols
- ✅ S3 bucket for email storage
- ✅ Secrets Manager for credentials
- ✅ CloudWatch monitoring and alarms
- ✅ IAM roles with least privilege
- ✅ Automatic SSL certificate generation
- ✅ CloudWatch Logs integration
- ✅ Optional Route53 DNS configuration
┌─────────────────────────────────────────────────────────────┐
│ AWS Cloud │
│ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ VPC (10.0.0.0/16) │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────┐ │ │
│ │ │ Public Subnet (10.0.1.0/24) │ │ │
│ │ │ │ │ │
│ │ │ ┌────────────────────────────────────────────┐ │ │ │
│ │ │ │ │ │ │ │
│ │ │ │ EC2 Instance (Mail Server) │ │ │ │
│ │ │ │ │ │ │ │
│ │ │ │ Ports: 25, 465, 587 (SMTP) │ │ │ │
│ │ │ │ 143, 993 (IMAP) │ │ │ │
│ │ │ │ 110, 995 (POP3) │ │ │ │
│ │ │ │ 80, 443 (HTTP/S) │ │ │ │
│ │ │ │ 8080, 8443 (WebSocket) │ │ │ │
│ │ │ │ │ │ │ │
│ │ │ └──────────────┬───────────────────────────────┘ │ │ │
│ │ │ │ │ │ │
│ │ └─────────────────┼──────────────────────────────────┘ │ │
│ │ │ │ │
│ └────────────────────┼────────────────────────────────────┘ │
│ │ │
│ ┌────────────────────┼────────────────────────────────────┐ │
│ │ AWS Services │ │ │
│ │ │ │ │
│ │ ┌─────────────┐ │ ┌──────────────┐ │ │
│ │ │ S3 Bucket │◄──┴───┤ IAM Role │ │ │
│ │ │ (Emails) │ │ │ │ │
│ │ └─────────────┘ └──────────────┘ │ │
│ │ │ │
│ │ ┌─────────────┐ ┌──────────────┐ │ │
│ │ │ Secrets │ │ CloudWatch │ │ │
│ │ │ Manager │ │ Logs/Alarms │ │ │
│ │ └─────────────┘ └──────────────┘ │ │
│ │ │ │
│ │ ┌─────────────┐ ┌──────────────┐ │ │
│ │ │ Route53 │ │ Certificate │ │ │
│ │ │ (Optional) │ │ Manager │ │ │
│ │ └─────────────┘ └──────────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
-
Bun (latest)
bun --version # Install from https://bun.sh -
AWS Credentials configured in
~/.aws/credentialsor environment variables# Option 1: AWS CLI aws configure # Option 2: Environment variables export AWS_ACCESS_KEY_ID=your-key export AWS_SECRET_ACCESS_KEY=your-secret export AWS_REGION=us-east-1
- AWS Account: Active AWS account with appropriate permissions
- AWS Credentials: Configured via
aws configureor environment variables - EC2 Key Pair (Optional but recommended):
aws ec2 create-key-pair --key-name mail --query 'KeyMaterial' --output text > ~/.ssh/mail.pem chmod 400 ~/.ssh/mail.pem
Your AWS user/role needs:
- EC2 full access
- VPC creation
- S3 bucket management
- IAM role creation
- Secrets Manager access
- CloudWatch Logs access
- Route53 access (if using custom domain)
cd infra
bun installCreate a .env file in the infra directory:
# AWS Configuration
AWS_ACCOUNT=123456789012
AWS_REGION=us-east-1
# Environment
ENVIRONMENT=dev
# EC2 Configuration
KEY_PAIR_NAME=mail
# Domain Configuration (required for production email)
DOMAIN_NAME=mail.example.com
HOSTED_ZONE_ID=Z1234567890ABCpantry run deploy:devThis will:
- Create VPC and networking
- Launch EC2 instance with security groups
- Create S3 bucket for backups
- Install and build the mail server from source
- Obtain Let's Encrypt TLS certificate (if domain configured)
- Configure SES with DKIM signing
- Set up DNS records (MX, SPF, DMARC, A) in Route 53
- Configure fail2ban, certbot auto-renewal, and log rotation
- Start all services (SMTP, IMAP, CalDAV)
Deployment Time: ~10-15 minutes
All configuration is centralized in config.ts - no need to edit stack code!
See CONFIG_INTEGRATION.md for complete documentation.
Edit infra/config.ts to customize:
export const config: SmtpServerConfig = {
// Change Zig version
zigVersion: '0.15.1',
// Update your repository
gitRepository: 'https://github.com/stacksjs/mail.git',
// Environment-specific settings
environments: {
dev: {
instanceType: 't3.small',
volumeSize: 30,
// ... more settings
},
},
// SMTP server limits
smtpServer: {
port: 2525,
maxConnections: 1000,
maxMessageSize: 52428800, // 50MB
// ... more settings
},
}bun run deploy:dev- Instance: t3.small (configurable in
config.environments.dev.instanceType) - Volume: 30 GB (configurable in
config.environments.dev.volumeSize) - Monitoring: Disabled
- Backups: Disabled
- Cost: ~$15-20/month
bun run deploy:staging- Instance: t3.medium (configurable in
config.environments.staging.instanceType) - Volume: 50 GB (configurable in
config.environments.staging.volumeSize) - Monitoring: Enabled
- Backups: Enabled
- Cost: ~$40-50/month
bun run deploy:prod- Instance: t3.large (configurable in
config.environments.production.instanceType) - Volume: 100 GB (configurable in
config.environments.production.volumeSize) - Monitoring: Full
- Backups: Required
- Cost: ~$80-100/month
All settings are defined in config.ts:
- Instance Types:
config.environments[env].instanceType - Volume Sizes:
config.environments[env].volumeSize - Network Ports:
config.ports.* - SMTP Settings:
config.smtpServer.* - Security:
config.security.* - Paths:
config.paths.* - Installation:
config.zigVersion,config.gitRepository
Use pre-configured presets for common scenarios:
import { presets } from './config';
// Cost-optimized (smaller instances)
const devConfig = { ...getEnvironmentConfig('dev'), ...presets.costOptimized.dev };
// High-performance (larger instances)
const prodConfig = { ...getEnvironmentConfig('production'), ...presets.highPerformance.production };You can still override settings using CDK context:
# Custom instance type
cdk deploy --context instanceType=t3.xlarge
# Custom key pair
cdk deploy --context keyPair=my-keypair
# Custom domain
cdk deploy --context domainName=smtp.mycompany.com --context hostedZoneId=Z123...Before deploying to production, validate your configuration:
VALIDATE_CONFIG=true npm run deploy:prodThis checks for:
- SSH security (warns if open to 0.0.0.0/0)
- Placeholder values that need updating
- Volume sizes that may be too small
npm run deploy:devnpm run deploy:staging# Review changes first
npm run diff:prod
# Deploy with manual approval
npm run deploy:prod# See what will be created/changed
npm run synth:dev
npm run diff:devAfter deployment, you'll see:
Outputs:
SmtpServerDevStack.InstanceId = i-0123456789abcdef0
SmtpServerDevStack.PublicIp = 54.123.45.67
SmtpServerDevStack.PublicDnsName = ec2-54-123-45-67.compute-1.amazonaws.com
SmtpServerDevStack.BucketName = mail-emails-dev-123456789012
SmtpServerDevStack.SecretArn = arn:aws:secretsmanager:...
SmtpServerDevStack.SshCommand = ssh -i ~/.ssh/mail.pem ec2-user@54.123.45.67
SmtpServerDevStack.SecurityGroupId = sg-0123456789abcdef0
SmtpServerDevStack.LogGroupName = /aws/ec2/mail-dev
ssh -i ~/.ssh/mail.pem ec2-user@<PUBLIC_IP>aws ssm start-session --target <INSTANCE_ID># SSH into instance
ssh -i ~/.ssh/mail.pem ec2-user@<PUBLIC_IP>
# Check SMTP service
sudo systemctl status mail
# View logs
sudo journalctl -u mail -f
# Check CloudWatch logs
aws logs tail /aws/ec2/mail-dev --followView metrics in AWS Console:
- Navigate to CloudWatch > Dashboards
- Namespace:
MailServer/<environment>
-
CPU Usage
- Threshold: 80%
- Alarm: CPU > 80% for 2 periods
-
Memory Usage
- Monitored via CloudWatch Agent
- Threshold: 80%
-
Disk Usage
- Monitored per volume
- Threshold: 85%
-
Instance Health
- Status checks
- Automatic recovery enabled
# Via AWS CLI
aws logs tail /aws/ec2/mail-dev --follow
# Via CloudWatch Console
# Navigate to CloudWatch > Log Groups > /aws/ec2/mail-<env>
# On instance
sudo tail -f /var/log/mail/mail.logConfigured alarms will send notifications when:
- CPU > 80% for 10 minutes
- Status check fails
- Disk usage > 85%
# Create key pair
aws ec2 create-key-pair --key-name mail --query 'KeyMaterial' --output text > ~/.ssh/mail.pem
chmod 400 ~/.ssh/mail.pem
# Deploy without key pair (use SSM instead)
cdk deploy --context keyPair=""# Check security group rules
aws ec2 describe-security-groups --group-ids <SG_ID>
# Use SSM Session Manager instead
aws ssm start-session --target <INSTANCE_ID># SSH to instance
ssh -i ~/.ssh/mail.pem ec2-user@<PUBLIC_IP>
# Check service status
sudo systemctl status mail
# View logs
sudo journalctl -u mail -n 100
# Check user data execution
sudo cat /var/log/user-data.log
# Restart service
sudo systemctl restart mail# Check IAM role policies
aws iam list-role-policies --role-name <ROLE_NAME>
# Verify bucket policy
aws s3api get-bucket-policy --bucket <BUCKET_NAME># Check memory usage
free -h
# Upgrade instance type
cdk deploy --context instanceType=t3.large# Enable CDK debug output
cdk deploy --debug
# Enable verbose AWS CLI output
aws --debug <command># View CloudFormation events
aws cloudformation describe-stack-events --stack-name mail-dev
# View EC2 system logs
aws ec2 get-console-output --instance-id <INSTANCE_ID>| Resource | Cost |
|---|---|
| EC2 t3.small | ~$15/month |
| EBS 30 GB | ~$3/month |
| S3 storage (1 GB) | ~$0.02/month |
| Data transfer (minimal) | ~$1/month |
| Total | ~$20/month |
| Resource | Cost |
|---|---|
| EC2 t3.large | ~$60/month |
| EBS 100 GB | ~$10/month |
| S3 storage (10 GB) | ~$0.23/month |
| CloudWatch Logs | ~$5/month |
| Data transfer | ~$10/month |
| Total | ~$85/month |
# Use Savings Plans
# - 1-year: Save 30%
# - 3-year: Save 50%
# Stop development instances when not in use
aws ec2 stop-instances --instance-ids <INSTANCE_ID>
# Use Spot Instances (dev/staging only)
# Modify instance market options in stack✅ Network Security
- VPC with public/private subnets
- Security groups with least privilege
- IMDS v2 required
✅ Data Security
- EBS encryption enabled
- S3 bucket encryption (SSE-S3)
- Secrets Manager for credentials
✅ Access Control
- IAM roles with minimal permissions
- Systems Manager for SSH-less access
- Optional: Restrict SSH by IP
✅ Monitoring
- CloudWatch Logs enabled
- CloudWatch Alarms configured
- fail2ban for intrusion prevention
// In cdk.json or via context
{
"sshAllowedCidrs": ["YOUR_OFFICE_IP/32"]
}aws iam enable-mfa-device --user-name <username> --serial-number <device-arn> --authentication-code1 <code1> --authentication-code2 <code2># Rotate Secrets Manager secret
aws secretsmanager rotate-secret --secret-id mail-credentials-prod# List security group rules
aws ec2 describe-security-groups --group-ids <SG_ID>aws guardduty create-detector --enable# Pull latest changes
git pull
# Update dependencies
npm update
# Review changes
npm run diff:prod
# Deploy updates
npm run deploy:prod- S3 bucket versioning: Enabled
- EBS snapshots: Via AWS Backup (recommended)
- Database backups: Automatic via Mail Server
# Create EBS snapshot
aws ec2 create-snapshot --volume-id <VOLUME_ID> --description "Manual backup"
# Export S3 bucket
aws s3 sync s3://<BUCKET_NAME> ./backup/# Development
npm run destroy:dev
# Staging
npm run destroy:staging
# Production (requires confirmation)
npm run destroy:prod- GitHub Issues: https://github.com/yourusername/mail/issues
- AWS Support: https://console.aws.amazon.com/support
MIT License - see LICENSE file for details.
Maintained by: DevOps Team Last Updated: 2025-10-24 Version: 1.0.0