☑️ Prerequisites
- You have created a Strapi project to deploy on AWS
- You have read the configuration documentation
- You understand the basics of AWS IAM and core security best practices
☁️ Strapi Cloud
You can also use Strapi Cloud to deploy and host your project quickly.
Security First IAM Setup
1. Create IAM User with Minimal Permissions
Security Note: The official Strapi guide suggests broad permissions. The following approach applies hardened, minimal, service specific permissions.
Create Administrator User (One time setup)
- Login as root
- Create an Administrator role following the AWS IAM Admin User Guide https://docs.aws.amazon.com/IAM/latest/UserGuide/getting-started_create-admin-group.html
Create Strapi Developer User with Hardened Permissions
Instead of using *FullAccess policies, create custom policies with only the required capabilities.
EC2 Minimal Policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:DescribeInstances",
"ec2:StartInstances",
"ec2:StopInstances",
"ec2:RebootInstances"
],
"Resource": "*"
}
]
}
RDS Minimal Policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"rds:DescribeDBInstances",
"rds:DescribeDBClusters"
],
"Resource": "*"
}
]
}
S3 Secure Policy (replace with your bucket name):
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "StrapiS3Access",
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:ListBucket",
"s3:DeleteObject",
"s3:PutObjectAcl"
],
"Resource": [
"arn:aws:s3:::s3-bucket-name",
"arn:aws:s3:::s3-bucket-name/*"
]
}
]
}
Secrets Manager Policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"secretsmanager:GetSecretValue"
],
"Resource": "arn:aws:secretsmanager:your-region:your-account-id:secret:your-secret-name*"
}
]
}
SES Policy (if using emails):
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ses:SendEmail",
"ses:SendRawEmail"
],
"Resource": "*"
}
]
}
AWS VPC Setup
Follow Strapi's original VPC guide but ensure you:
- Select the correct AWS region
- Use two Availability Zones for redundancy
- Configure two public subnets and two private subnets
- Enable DNS hostnames and DNS resolution
EC2 Instance with IAM Role (Recommended)
1. Create IAM Role for EC2
Instead of access keys, assign an IAM role to your instance.
- Go to IAM → Roles → Create Role
- Select EC2
- Attach the minimal policies created earlier
- Name the role
StrapiEC2Role
2. Launch EC2 Instance
- Choose Ubuntu Server 22.04 LTS
- Use t2.small or higher
- Attach the role
StrapiEC2Role -
Configure security groups with minimal ports:
- SSH 22 restricted to your IP
- HTTP 80
- HTTPS 443
- Custom TCP 1337 only for testing
Security Warning: Remove port 1337 after configuring NGINX.
Secure RDS Database Setup
1. Create PostgreSQL Database
Security best practices:
- Use the latest PostgreSQL engine
- Select Production templates
- Connect RDS to your VPC
- Use private subnets
- Enable encryption at rest
- Enable automated backups
- Enable Performance Insights
2. Database Security Best Practices
- Use strong passwords longer than 20 characters
- Encrypt data in transit
- Restrict RDS access to only your EC2 security group
- Apply routine updates
Secure S3 Configuration
Critical S3 Security Update
Avoid the public bucket policy in the original guide. It exposes all actions publicly.
1. Create Private S3 Bucket
- Enable “Block all public access”
- Enable versioning
- Enable encryption
2. Secure Access Options
Option A: Private with Signed URLs (Recommended)
Uses temporary signed URLs for access.
Option B: Public Read Only (if required)
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::your-bucket-name/*"
}
]
}
This allows reading but not modifying files.
Secure Environment Variable Management
1. AWS Secrets Manager Setup
Store sensitive variables in Secrets Manager.
Create a secret:
aws secretsmanager create-secret \
--name "strapi/production/env" \
--description "Strapi production environment variables" \
--secret-string file://secret.json \
--region us-east-1
Example secret.json:
{
"APP_KEYS": "your-app-keys",
"API_TOKEN_SALT": "your-api-token-salt",
"ADMIN_JWT_SECRET": "your-admin-jwt-secret",
"JWT_SECRET": "your-jwt-secret",
"DATABASE_HOST": "your-rds-endpoint.rds.amazonaws.com",
"DATABASE_PORT": "5432",
"DATABASE_NAME": "strapi",
"DATABASE_USERNAME": "postgres",
"DATABASE_PASSWORD": "your-secure-password",
"AWS_REGION": "us-east-1",
"AWS_BUCKET_NAME": "your-bucket-name"
}
2. Environment Fetch Scripts
Goal: never commit .env files to Git.
Option A: Bash Script
#!/bin/bash
# fetch-env.sh
SECRET_NAME="strapi/production/env"
REGION="us-east-1"
OUTPUT_FILE=".env"
echo "Fetching secrets..."
if ! command -v aws >/dev/null; then
echo "AWS CLI missing"
exit 1
fi
if ! command -v jq >/dev/null; then
echo "jq missing"
exit 1
fi
SECRET_JSON=$(aws secretsmanager get-secret-value \
--secret-id "$SECRET_NAME" \
--region "$REGION" \
--query SecretString \
--output text 2>/dev/null)
echo "$SECRET_JSON" | jq -r 'to_entries|map("\(.key)=\(.value)")|.[]' > "$OUTPUT_FILE"
Make executable:
chmod +x fetch-env.sh
./fetch-env.sh
Option B: Node.js Script
const fs = require('fs');
const { SecretsManagerClient, GetSecretValueCommand } = require('@aws-sdk/client-secrets-manager');
const client = new SecretsManagerClient({
region: process.env.AWS_REGION || 'us-east-1'
});
async function fetchSecret(secretName, outputPath = '.env') {
try {
const command = new GetSecretValueCommand({ SecretId: secretName });
const response = await client.send(command);
const secret = JSON.parse(response.SecretString);
const envData = Object.entries(secret)
.map(([key, val]) => `${key}=${val}`)
.join('\n');
fs.writeFileSync(outputPath, envData);
} catch (err) {
console.error('Error fetching secret:', err.message);
process.exit(1);
}
}
const secretName = process.argv[2] || 'strapi/production/env';
fetchSecret(secretName);
Install dependencies:
npm install @aws-sdk/client-secrets-manager
node fetch-secrets.js
3. Secure PM2 Configuration
module.exports = {
apps: [
{
name: 'strapi-secure',
cwd: '/home/ubuntu/your-strapi-project',
script: 'npm',
args: 'start',
env_file: '/home/ubuntu/your-strapi-project/.env'
}
]
};
Node.js and Application Setup
1. Install Dependencies
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
NODE_MAJOR=20
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list
sudo apt-get update
sudo apt-get install -y nodejs
npm install -g @aws-sdk/client-secrets-manager
mkdir ~/.npm-global
npm config set prefix '~/.npm-global'
echo 'export PATH=~/.npm-global/bin:$PATH' >> ~/.profile
source ~/.profile
2. Deploy Strapi Securely
git clone https://github.com/your-org/your-strapi-repo.git
cd your-strapi-repo
npm install
node fetch-secrets.js
NODE_ENV=production npm run build
npm install pm2@latest -g
pm2 start ecosystem.config.js
pm2 save
pm2 startup
Production Security Checklist
Before Going Live
- Remove port 1337 access
- Configure NGINX reverse proxy with TLS
- Enable CloudWatch logging
- Enable automated RDS and S3 backups
- Enable AWS WAF
- Enable VPC Flow Logs
- Configure AWS Config
- Configure log aggregation and automated patches
- Enable CloudTrail auditing
Ongoing Security
- Update EC2 packages regularly
- Rotate database passwords every quarter
- Monitor CloudTrail logs
- Review IAM permissions often
- Update Strapi and dependencies
- Test backups and recovery procedures
Complete Git Workflow
Commit to Git
fetch-env.sh
fetch-secrets.js
.env.example
.gitignore
deploy.sh
package.json
/src/
/config/
ecosystem.config.js
Never Commit to Git
.env
.env.local
.aws/
secret.json
Recommended .gitignore
.env
.env.*
!.env.example
secret.json
.aws/
Deployment Automation
1. deploy.sh
#!/bin/bash
set -e
SECRET_NAME="strapi/production/env"
REGION="us-east-1"
APP_DIR="/home/ubuntu/your-strapi-project"
PM2_CONFIG="/home/ubuntu/ecosystem.config.js"
cd "$APP_DIR"
git pull origin main
npm ci --production
if [ -f "fetch-env.sh" ]; then
./fetch-env.sh
else
node fetch-secrets.js "$SECRET_NAME"
fi
NODE_ENV=production npm run build
pm2 restart "$PM2_CONFIG" || pm2 start "$PM2_CONFIG"
pm2 save
2. EC2 Deployment
git clone https://github.com/your-org/your-strapi-repo.git
cd your-strapi-repo
chmod +x fetch-env.sh deploy.sh
npm install
./deploy.sh
Subsequent Deployments
cd /home/ubuntu/your-strapi-project
./deploy.sh
3. Webhook Integration
const secret = 'your_webhook_secret';
const repo = '/home/ubuntu/your-strapi-project/';
const http = require('http');
const crypto = require('crypto');
const exec = require('child_process').exec;
http.createServer(function(req, res) {
req.on('data', function(chunk) {
let sig = 'sha1=' + crypto
.createHmac('sha1', secret)
.update(chunk.toString())
.digest('hex');
if (req.headers['x-hub-signature'] == sig) {
exec(`cd ${repo} && ./deploy.sh`);
}
});
res.end();
}).listen(8080);
Team Development Workflow
Setup for New Members
Initial Setup
git clone https://github.com/your-org/your-strapi-repo.git
cd your-strapi-repo
npm install
aws configure
Generate Environment File
./fetch-env.sh
node fetch-secrets.js strapi/development/env
node fetch-secrets.js strapi/staging/env
Start Development
npm run develop
Developer README Section
## Environment Setup
This project uses AWS Secrets Manager.
### Quick Start
1. Clone the repository
2. Run npm install
3. Run ./fetch-env.sh
4. Run npm run develop
Cost Optimization
AWS Secrets Manager Pricing
- 0.40 dollars per secret per month
- 0.05 dollars per 10,000 API calls
- First 30 days free
Example cost:
- Storage 0.40 dollars
- API calls 0.005 dollars
- Total about 0.41 dollars per month
Cost Saving Tips
- Use EC2 IAM roles
- Tag resources properly
- Use RDS reserved instances
- Enable S3 lifecycle policies
- Monitor costs using Cost Explorer
Troubleshooting
1. IAM Permission Errors
- Ensure IAM role is attached to EC2
- Ensure policies contain required actions
- Check resource ARNs
2. Secrets Manager Issues
- Check secret name and region
- Verify IAM permissions
- Check AWS SDK credentials
3. S3 Upload Issues
- Check bucket policy
- Check CORS
- Check IAM permissions
If you want, I can also format this for GitHub README, generate a table of contents, or optimize the Markdown for SEO on dev.to.
Top comments (0)