Production-ready Mattermost deployment on AWS with OpenTofu
Enterprise-grade team collaboration platform for Mattermost deployed on AWS
Features β’ Quick Start β’ Architecture β’ Documentation
This repository contains Infrastructure as Code (IaC) for deploying a complete Mattermost collaboration platform on AWS using OpenTofu with production-ready security, monitoring, and scalability.
β Secure by Default - TLS 1.3, encryption at rest/transit, private subnets β Highly Available - Multi-AZ deployment with Application Load Balancer β Scalable - Easy vertical/horizontal scaling options β Observable - CloudWatch dashboards, logs, and alarms β Cost Optimized - ~$79-90/month with VPC endpoints and optional NAT β Battle Tested - Pre-commit hooks, security scanning, automated testing
| Component | Configuration |
|---|---|
| Region | Configurable (default: us-west-1) |
| Domain | mattermost.example.com |
| Compute | Docker on EC2 (t3.medium) |
| Database | PostgreSQL 16 on RDS (db.t4g.micro) |
| Storage | S3 with versioning and lifecycle policies |
| Security | Locked down signups, email verification, MFA enabled |
| AWS SES SMTP integration | |
| Monitoring | CloudWatch dashboards, logs, and alarms |
High-level architecture of the Mattermost deployment:
graph TB
Internet([π Internet])
ALB[Application Load Balancer<br/>Public Subnets]
MM1[π³ Mattermost<br/>Docker Container<br/>EC2 - Private Subnet AZ-A]
MM2[π³ Mattermost<br/>Docker Container<br/>EC2 - Private Subnet AZ-B<br/><i>Future Scaling</i>]
RDS[(ποΈ PostgreSQL 16<br/>RDS<br/>Private Subnet)]
S3[π¦ S3 Bucket<br/>File Storage<br/>Versioned + Encrypted]
NAT[π NAT Gateway]
Internet -->|HTTPS/TLS 1.3| ALB
ALB -->|HTTP:8065| MM1
ALB -.->|Future| MM2
MM1 -->|PostgreSQL| RDS
MM2 -.->|PostgreSQL| RDS
MM1 -->|S3 API| S3
MM2 -.->|S3 API| S3
MM1 -.->|Outbound| NAT
MM2 -.->|Outbound| NAT
NAT -.->|Internet<br/>Updates/SES| Internet
classDef public fill:#e1f5ff,stroke:#01579b
classDef private fill:#fff3e0,stroke:#e65100
classDef storage fill:#f3e5f5,stroke:#4a148c
class ALB,NAT public
class MM1,MM2,RDS private
class S3 storage
graph LR
subgraph VPC["π’ VPC (10.0.0.0/16)"]
subgraph AZ1["Availability Zone A"]
PubSub1[Public Subnet<br/>10.0.1.0/24]
PrivSub1[Private Subnet<br/>10.0.11.0/24]
end
subgraph AZ2["Availability Zone B"]
PubSub2[Public Subnet<br/>10.0.2.0/24]
PrivSub2[Private Subnet<br/>10.0.12.0/24]
end
PubSub1 --> PrivSub1
PubSub2 --> PrivSub2
end
IGW[Internet Gateway] --> PubSub1
IGW --> PubSub2
PrivSub1 --> NAT[NAT Gateway]
PrivSub2 --> NAT
NAT --> IGW
mattermost-ops-kit/
β
βββ π README.md # You are here
βββ π BOOTSTRAP.md # First-time AWS setup guide
βββ π DEVELOPMENT.md # Developer environment setup
βββ π CONTRIBUTING.md # Contribution guidelines
βββ π CHANGELOG.md # Module/API change history
βββ π§ͺ examples/ # Starter tfvars profiles
β
βββ ποΈ tofu/ # OpenTofu infrastructure code
βββ π§ main.tf # Provider and common resources
βββ ποΈ variables.tf # Input variables
βββ π outputs.tf # Output values
βββ π vpc.tf # Network infrastructure (VPC, subnets, NAT)
βββ π security-groups.tf # Security rules and firewall
βββ π€ iam.tf # IAM roles and policies
βββ ποΈ rds.tf # PostgreSQL database
βββ π¦ s3.tf # S3 buckets for file storage
βββ π₯οΈ ec2.tf # EC2 compute instances
βββ π user-data.sh # Docker installation script
βββ βοΈ alb.tf # Application Load Balancer
βββ π acm.tf # SSL/TLS certificates
βββ π cloudwatch.tf # Monitoring and alerting
βββ π§ ses.tf # Email notifications (AWS SES)
βββ π tofu.tfvars.example # Canonical example configuration
βββ ποΈ backend.hcl.example # Optional remote state backend template
βββ π README.md # Module API reference
Before you begin, ensure you have:
- AWS Account with administrative access
- Domain Registration for example.com
- AWS CLI installed and configured
- OpenTofu >= 1.6 installed
- Basic knowledge of AWS and OpenTofu
For Contributors: If you're developing or modifying the infrastructure code, see DEVELOPMENT.md for complete local setup instructions including all linters and security scanners.
For deployment only, you need OpenTofu and AWS CLI. For development, see DEVELOPMENT.md for full tool setup.
OpenTofu:
# macOS
brew install opentofu
# Linux - Install script
curl -fsSL https://get.opentofu.org/install-opentofu.sh | sudo bash
# Or download binary manually
# Visit: https://opentofu.org/docs/intro/install/AWS CLI:
# macOS
brew install awscli
# Linux
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/installVerify Installation:
tofu version # Should show OpenTofu v1.6+
aws --version # Should show AWS CLI v2.xaws configure
# AWS Access Key ID: [your-access-key]
# AWS Secret Access Key: [your-secret-key]
# Default region name: us-west-1
# Default output format: jsonVerify your configuration:
aws sts get-caller-identitycd tofu
cp tofu.tfvars.example tofu.tfvars
# Or start from a profile:
# cp ../examples/production.tofu.tfvars.example tofu.tfvarsEdit tofu/tofu.tfvars with your values:
# Required variables
domain_name = "mattermost.example.com"
alert_email = "admin@example.com"
ssh_key_name = "mattermost-key" # Create this in AWS EC2 console first
# Optional: Set a custom database password or leave empty for auto-generation
db_password = ""
# Optional: Configure SES email
ses_email_identity = "noreply@example.com"cd tofu
# Initialize OpenTofu
tofu init
# Optional remote state:
# cp backend.hcl.example backend.hcl
# tofu init -backend-config=backend.hcl
# Review the deployment plan
tofu plan
# Deploy infrastructure
tofu applyType yes when prompted to confirm.
New to the project? Here's how to get started making changes to the infrastructure.
-
Install required tools:
# macOS brew install opentofu awscli pre-commit tflint trivy checkov shellcheck shfmt # Linux (Ubuntu/Debian) # See DEVELOPMENT.md for detailed instructions
-
Configure AWS credentials:
aws configure # Enter your AWS access key, secret key, and set region to us-west-1 -
Clone and setup the repository:
git clone <repository-url> cd mattermost-ops-kit # Install pre-commit hooks (runs quality checks on every commit) pre-commit install # Initialize OpenTofu cd tofu tofu init
-
Create a feature branch:
git checkout -b feature/your-change-name
-
Make your changes to
.tffiles in thetofu/directory -
Test locally:
# Format code tofu fmt -recursive # Validate syntax tofu validate # Run security checks make check # or: pre-commit run --all-files # Preview changes tofu plan
-
Commit your changes:
git add . git commit -m "feat: description of your change" # Pre-commit hooks will run automatically
cd tofu
# Always review the plan first
tofu plan
# Apply changes (requires confirmation)
tofu apply
# Monitor deployment
aws logs tail /aws/mattermost/mattermost-prod --followAccess the EC2 instance:
aws ssm start-session --target $(cd tofu && tofu output -raw ec2_instance_id)View Mattermost logs:
# Via CloudWatch
aws logs tail /aws/mattermost/mattermost-prod --follow
# Or directly on instance
aws ssm start-session --target $(cd tofu && tofu output -raw ec2_instance_id)
sudo docker logs mattermost -fVerify S3 file storage:
aws s3 ls s3://$(cd tofu && tofu output -raw s3_bucket_name)/ --recursiveRestart Mattermost:
aws ssm start-session --target $(cd tofu && tofu output -raw ec2_instance_id)
sudo systemctl restart mattermost- Full setup guide: See DEVELOPMENT.md for complete tool installation
- First-time deployment: See BOOTSTRAP.md for AWS account setup
- Contributing guidelines: See CONTRIBUTING.md for code standards
- Architecture details: See tofu/README.md for technical documentation
For security, create a dedicated IAM user instead of using root credentials:
-
Log into AWS Console
-
Go to IAM > Users > Add User
-
User name:
tofu-deployer -
Enable: "Programmatic access"
-
Attach policies:
AdministratorAccess(for initial setup)- Or use a custom policy with minimum required permissions
-
Save the Access Key ID and Secret Access Key
# In AWS Console: EC2 > Key Pairs > Create Key Pair
# Name: mattermost-key
# Type: RSA
# Format: .pem
# Or via CLI:
aws ec2 create-key-pair \
--key-name mattermost-key \
--query 'KeyMaterial' \
--output text > ~/.ssh/mattermost-key.pem
chmod 400 ~/.ssh/mattermost-key.pemRegister example.com with a domain registrar:
- Choose a registrar (e.g., Route53, Namecheap, GoDaddy)
- Register example.com
- Note the nameservers
# Create hosted zone
aws route53 create-hosted-zone --name example.com --caller-reference $(date +%s)
# Note the nameservers from output
# Update your domain registrar to use these nameserversAfter deployment, create DNS records manually.
For production, store OpenTofu state in S3:
# Create S3 bucket for state
aws s3 mb s3://mattermost-ops-tofu-state --region us-west-1
# Enable versioning
aws s3api put-bucket-versioning \
--bucket mattermost-ops-tofu-state \
--versioning-configuration Status=Enabled
# Enable encryption
aws s3api put-bucket-encryption \
--bucket mattermost-ops-tofu-state \
--server-side-encryption-configuration '{
"Rules": [{
"ApplyServerSideEncryptionByDefault": {
"SSEAlgorithm": "AES256"
}
}]
}'
# Create DynamoDB table for state locking
aws dynamodb create-table \
--table-name mattermost-ops-tofu-locks \
--attribute-definitions AttributeName=LockID,AttributeType=S \
--key-schema AttributeName=LockID,KeyType=HASH \
--billing-mode PAY_PER_REQUEST \
--region us-west-1Create tofu/backend.hcl with your backend settings:
bucket = "mattermost-ops-tofu-state"
key = "mattermost/tofu.tfstate"
region = "us-west-1"
encrypt = true
dynamodb_table = "mattermost-ops-tofu-locks"Then initialize with backend config:
tofu init -backend-config=backend.hclIf you want email notifications:
# Verify email identity
aws ses verify-email-identity \
--email-address noreply@example.com \
--region us-west-1
# Check your inbox and click verification link
# Request production access (to move out of sandbox)
# Go to AWS Console > SES > Account Dashboard > Request production accesscd tofu
# Initialize (first time only)
tofu init -backend-config=backend.hcl
# Validate configuration
tofu validate
# See what will be created
tofu plan
# Deploy (will take ~15-20 minutes)
tofu apply
# Save outputs
tofu output > outputs.txtIf using Route53:
# Get ALB DNS name from tofu output
ALB_DNS=$(tofu output -raw alb_dns_name)
ALB_ZONE_ID=$(tofu output -raw alb_zone_id)
HOSTED_ZONE_ID="Z1234567890ABC" # Your Route53 zone ID
# Create alias record
aws route53 change-resource-record-sets \
--hosted-zone-id $HOSTED_ZONE_ID \
--change-batch '{
"Changes": [{
"Action": "CREATE",
"ResourceRecordSet": {
"Name": "mattermost.example.com",
"Type": "A",
"AliasTarget": {
"HostedZoneId": "'$ALB_ZONE_ID'",
"DNSName": "'$ALB_DNS'",
"EvaluateTargetHealth": true
}
}
}]
}'If using external DNS:
Create an A or CNAME record:
- Name:
mattermost - Type:
CNAME - Value:
[ALB DNS from tofu output] - TTL:
300
# Check certificate status
aws acm describe-certificate \
--certificate-arn $(tofu output -raw acm_certificate_arn) \
--region us-west-1
# Add DNS validation record if neededFor DNS validation, add the CNAME record shown in the certificate details to your DNS provider.
Check the email you provided for SNS subscription confirmation and click the link.
Once DNS propagates (5-30 minutes):
- Visit:
https://mattermost.example.com - Create first admin account
- Configure your workspace
tofu show
tofu state list# Modify tofu.tfvars or .tf files
vim tofu/tofu.tfvars
# Preview changes
tofu plan
# Apply changes
tofu apply# CloudWatch logs
aws logs tail /aws/mattermost/mattermost-prod --follow --region us-west-1
# Access EC2 via Systems Manager
INSTANCE_ID=$(tofu output -raw ec2_instance_id)
aws ssm start-session --target $INSTANCE_ID --region us-west-1
# Inside the instance
sudo docker logs mattermost -f
sudo journalctl -u mattermost -f# SSH to instance
aws ssm start-session --target $(tofu output -raw ec2_instance_id)
# Update docker-compose
cd /opt/mattermost
sudo vim .env # Change MATTERMOST_VERSION=9.5.0
# Restart
sudo systemctl restart mattermost# Create RDS snapshot
aws rds create-db-snapshot \
--db-instance-identifier mattermost-prod-db \
--db-snapshot-identifier mattermost-backup-$(date +%Y%m%d) \
--region us-west-1
# List S3 versions (automatic due to versioning)
aws s3api list-object-versions \
--bucket $(tofu output -raw s3_bucket_name)As your user base grows, scale the infrastructure:
Increase instance sizes in tofu.tfvars:
ec2_instance_type = "t3.large" # 2 vCPU β 4 vCPU
db_instance_class = "db.t4g.small" # 1 GB β 2 GB RAMThen apply: tofu apply
Suitable for: 50-200 concurrent users
For larger scale (500+ users), consider:
- π Auto Scaling Group - Multiple EC2 instances with auto-scaling
- ποΈ Aurora PostgreSQL - Clustered database with read replicas
- β‘ ElastiCache Redis - Session caching and performance
- π CloudFront - CDN for static assets
- π Read Replicas - Offload read traffic from primary DB
Suitable for: 200+ concurrent users
- β Encryption at rest - RDS and S3 encrypted
- β Encryption in transit - TLS 1.3 for all HTTPS traffic
- β Private subnets - EC2 and RDS not directly accessible
- β Security groups - Least-privilege firewall rules
- β MFA support - Enabled for Mattermost users
- β Locked down signups - Admin-only user creation
- β Email verification - Required for all accounts
- β S3 versioning - File backup and recovery
- β VPC Flow Logs - Network traffic monitoring
- β CloudWatch monitoring - Logs and alarms
- β Systems Manager - Secure instance access (no SSH)
- Enable MFA for AWS root account
- Use least-privilege IAM policies for human users
- Enable AWS Config for compliance monitoring
- Enable GuardDuty for threat detection
- Set up AWS WAF on ALB for DDoS protection
- Regular security updates via Systems Manager Patch Manager
- Enable CloudTrail for audit logging
- Review security groups quarterly
- Implement AWS Backup for automated backups
- Set up AWS Security Hub for centralized security view
Estimated monthly costs in us-west-1 (Northern California):
| Resource | Configuration | Monthly Cost |
|---|---|---|
| π₯οΈ EC2 Instance | t3.medium | ~$35 |
| ποΈ RDS Database | db.t4g.micro, PostgreSQL 16 | ~$15 |
| βοΈ Application Load Balancer | Standard | ~$18 |
| π¦ S3 Storage | 10 GB + requests | ~$0.25 |
| π VPC Endpoints | S3 (free) + SES (~$7) | ~$7 |
| π CloudWatch | Logs + metrics | ~$2-5 |
| π Data Transfer | Outbound (via endpoints) | ~$2-10 |
| Estimated Total | π΅ ~$79-90/month |
π‘ Cost Savings: By using VPC endpoints and disabling the NAT Gateway by default, this deployment saves ~$32/month compared to traditional NAT-based architectures.
When you need to perform system updates or Docker pulls, temporarily enable the NAT Gateway:
| Additional Resource | Monthly Cost |
|---|---|
| π NAT Gateway (temporary, ~2 hours/month) | ~$0.20 |
| Total with occasional NAT | π΅ ~$79-91/month |
See docs/MAINTENANCE.md for how to enable/disable NAT Gateway for maintenance windows.
- ποΈ Use Reserved Instances - Save 40-60% on EC2 and RDS
- β° Schedule shutdowns - Stop non-production during off-hours
- ποΈ S3 lifecycle policies - Archive old files to Glacier
- π Monitor regularly - Use AWS Cost Explorer and billing alerts
- π Right-size resources - Adjust instance types based on actual usage
- π VPC endpoints - Already implemented for S3 and SES (saves data transfer costs)
- πͺ Keep NAT disabled - Only enable for maintenance windows (default configuration)
# Refresh state
tofu refresh
# Fix state drift
tofu apply -refresh-only
# View detailed logs
export TF_LOG=DEBUG
tofu apply# Check ALB target health
aws elbv2 describe-target-health \
--target-group-arn [arn-from-output] \
--region us-west-1
# Check EC2 instance status
aws ec2 describe-instance-status \
--instance-ids [instance-id] \
--region us-west-1
# Test from inside VPC
aws ssm start-session --target [instance-id]
curl http://localhost:8065/api/v4/system/ping# SSH to instance
aws ssm start-session --target [instance-id]
# Check Docker status
sudo systemctl status mattermost
sudo docker ps
sudo docker logs mattermost
# Restart services
sudo systemctl restart mattermostTo destroy all resources:
cd tofu
# Disable deletion protection first
vim rds.tf # Set deletion_protection = false
tofu apply # Apply the change
# Destroy everything
tofu destroyWarning: This will permanently delete all data. Ensure you have backups!
| Document | Description |
|---|---|
| π BOOTSTRAP.md | Complete first-time AWS setup and deployment guide |
| π DEVELOPMENT.md | Developer environment setup and tool installation |
| π CONTRIBUTING.md | Code standards, PR process, and quality checks |
| π tofu/README.md | Module API reference (inputs, outputs, behavior toggles) |
| π CHANGELOG.md | History of API and behavior changes |
| π§ͺ examples/README.md | Starter configuration profiles |
- Mattermost Documentation: docs.mattermost.com
- OpenTofu Documentation: opentofu.org/docs
- AWS Documentation: docs.aws.amazon.com
- OpenTofu AWS Provider: registry.opentofu.org/providers/hashicorp/aws
This is an open-source infrastructure repository. We welcome contributions.
# Install development tools and pre-commit hooks
make setup
# Run all quality checks
make check
# See all available commands
make helpThis project maintains high quality standards with:
- β Pre-commit hooks - Automatic checks before every commit
- β
OpenTofu formatting - Consistent code style (
tofu fmt) - β Security scanning - Trivy and Checkov for vulnerabilities
- β Shell linting - ShellCheck for bash scripts
- β Documentation linting - Markdown and YAML validation
- β CI/CD pipeline - All checks run on every PR
See CONTRIBUTING.md for detailed guidelines.
MIT License. See LICENSE.
Questions or Issues? Open an issue
Made with β€οΈ by Mattermost Ops Kit Infrastructure Team