Deploying a Strapi API on AWS (EC2 & RDS & S3)
Goal: build a HTTPS-secured Strapi API with dev & staging & prod modes
Everything done in the following is AWS Free Tier eligible.
Make sure you have already skimmed the Strapi docs before you start.
§ Create EC2 instance / RDS instance / S3 bucket
You can skim this section if you are familiar with AWS
⊙ EC2
- Click
Launch Instance
button - Choose AMI:
Ubuntu Server 18.04 LTS (HVM), SSD Volume Type
- Choose Instance Type:
General purpose, t2.micro
- Configure Instance: as you like
- Add Storage:
General Purpose SSD (gp2), 8 GB
- Add Tags: as you like
- Configure Security Group:
- SSH (22) -
My IP
orAnywhere
or as you like - HTTP (80) -
Anywhere
- HTTPS (443) -
Anywhere
- SSH (22) -
- Review: click
Launch
button, then a modal pops up. If you are a:- Newbie: Choose
Create a new key pair
namedstrapi-cms
, download it asstrapi-cms.pem
- Veteran: as you like
- Newbie: Choose
- Finally, click
Launch Instances
button
⊙ RDS
- Click
Create database
button - Select engine:
PostgreSQL
- Choose use case: as you like
- Specify DB details:
- DB engine version:
PostgreSQL 10.x-R1
- DB instance class:
db.t2.micro
- Multi-AZ deployment:
No
- Storage:
General Purpose (SSD), 20 GB
- DB instance identifier: as you like
- Master username: as you like
- Password: as you like, recommend https://passwordsgenerator.net
- DB engine version:
- Configure advanced settings
- Public accessibility:
Yes
(that's why you need a super strong password) - Database name:
strapi
- Monitoring & Maintenance window: choose an idle peroid in your timezone
- Click
Create database
button
- Public accessibility:
- Instance Details panel - Security groups
- Edit inbound rules: PostgreSQL (5432) -
Anywhere
- Edit inbound rules: PostgreSQL (5432) -
- Create databases for dev & staging modes (GUI recommend: https://dbeaver.io)
- Development mode:
strapi_dev
- Staging mode:
strapi_staging
- Production mode:
strapi
(already exists)
- Development mode:
⊙ S3
- Click
Create bucket
button - Name and region
- Bucket name: as you like
- Configure options: as you like
- Set permissions
-
-
Block new public ACLs and uploading public objects (Recommended)
-
-
-
Remove public access granted through public ACLs (Recommended)
-
-
-
Block new public bucket policies (Recommended)
-
-
-
Block public and cross-account access if bucket has public policies (Recommended)
-
Do not grant Amazon S3 Log Delivery group write access to this bucket
-
- Review: click
Create bucket
button
§ Point your domain to EC2
Point the A / CNAME records to the EC2's IPv4 Public IP / Public DNS (IPv4), such as:
- Development mode:
dev-cms.yourdomain.com
- Staging mode:
staging-cms.yourdomain.com
- Production mode:
cms.yourdomain.com
§ Warm up EC2
⊙ Login to EC2 & update
Switch to the directory where the key pair strapi-cms.pem
locates:
$ ssh -i strapi-cms.pem ubuntu@<ec2-public-ip>
$ sudo apt update
⊙ Install Nginx
$ sudo apt install nginx
⊙ Install nvm & Node.js & PM2
- Install nvm:
$ curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.34.0/install.sh | bash
- Install Node.js:
$ nvm install 10
- Install PM2:
$ npm i pm2 -g
⊙ Install Certbot
According to https://certbot.eff.org/lets-encrypt/ubuntubionic-nginx :
$ sudo apt-get install software-properties-common
$ sudo add-apt-repository universe
$ sudo add-apt-repository ppa:certbot/certbot
$ sudo apt-get update
$ sudo apt-get install python-certbot-nginx
§ Create a local Strapi project
⊙ Installation and initialization
Firstly, install Strapi (refer to https://strapi.io/documentation/3.x.x/getting-started/installation.html):
$ npm i -g strapi@alpha
Secondary, create a new project (refer to https://strapi.io/documentation/3.x.x/cli/CLI.html#strapi-new):
$ strapi new strapi-cms \
--dbclient=postgres \
--dbport=5432 \
--dbhost=<RDS endpoint> \
--dbname=strapi_dev \
--dbusername=<RDS master username> \
--dbpassword=<RDS password>
Finally, DO NOT rush to strapi start
for now.
There are some preparatory procedures to accomplish.
(By the way, you may be interested in https://strapi.io/documentation/3.x.x/advanced/usage-tracking.html)
⊙ Complete database configuration
If you prefer best practices like https://12factor.net/config and https://github.com/i0natan/nodebestpractices/blob/master/sections/projectstructre/configguide.md , you'd better use tools like dotenv instead.
strapi-cms/config/environments/development/database.json
has been completed during the initialization.
Complete strapi-cms/config/environments/{staging|production}/database.json
based on it. For example:
{
"defaultConnection": "default",
"connections": {
"default": {
"connector": "strapi-hook-bookshelf",
"settings": {
"client": "postgres",
"host": "<RDS endpoint>",
"port": 5432,
"database": "<strapi_staging|strapi>",
"username": "<RDS master username>",
"password": "<RDS password>"
},
"options": {
"ssl": false
}
}
}
}
⊙ Fix listening ports
Modify port
in strapi-cms/config/environments/{staging|production}/server.json
:
- development: 1337 (default)
- staging: 1338
- production: 1339
⊙ Install S3 upload plugin
Refer to https://strapi.io/documentation/3.x.x/guides/upload.html#install-providers
$ npm i -S strapi-provider-upload-aws-s3@alpha
⊙ Add npm scripts for staging & production mode
You can use PM2 - Ecosystem File instead if you'd like to
npm start
is for development mode by default.
You can set NODE_ENV
before it according to https://strapi.io/documentation/3.x.x/guides/deployment.html .
However, for the sake of compatibility, cross-env is introduced.
$ npm i -D cross-env
So strapi-cms/package.json
may look like:
{
...
"scripts": {
"setup": "cd admin && npm run setup",
"start": "node server.js", // for development mode
"staging": "cross-env NODE_ENV=staging npm start", // for staging mode
"prod": "cross-env NODE_ENV=production npm start", // for production mode
"strapi": "node_modules/strapi/bin/strapi.js",
"lint": "node_modules/.bin/eslint api/**/*.js config/**/*.js plugins/**/*.js",
"postinstall": "node node_modules/strapi/lib/utils/post-install.js"
},
...
}
nginx.conf
to the project root
⊙ Add If you prefer best practices using
/etc/nginx/{sites-available|sites-enabled}
, you may need help from https://nginxconfig.io or https://github.com/h5bp/server-configs-nginx . I prefer single filenginx.conf
because of simplicity.
Don't forget to replace all yourdomain.com
with yours.
⊙ Run
$ cd strapi-cms && strapi start
Your browser will open http://localhost:1337 automatically later.
Create the admin with a strong password.
⊙ Complete S3 settings
Visit PLUGINS > Files Upload to complete the S3 settings for all modes.
⊙ GraphQL
If you'd like to equip GraphQL, visit GENERAL > Marketplace to download.
Refer to https://strapi.io/documentation/3.x.x/guides/graphql.html for further info.
⊙ Configure response & security
Since Strapi will be running behind a well-tuned Nginx, you should:
- Visit GENERAL > Configurations > ENVIRONMENTS > Response, set Production & Staging's Gzip to
OFF
- Visit GENERAL > Configurations > ENVIRONMENTS > Security, copy all the settings from Development to Production & Staging
For more details, please turn to https://strapi.io/documentation/3.x.x/configurations/configurations.html
Github free private repo (or Gitlab, etc)
⊙ Push to a$ git init
$ git add -A
$ git commit -m 'init'
$ git remote add origin <private-git-repo-url>
$ git push -u origin master
§ Deploy Strapi on EC2
⊙ Pull the repo and install npm dependencies
$ cd ~
$ git clone <private-git-repo-url>
$ cd strapi-cms
$ npm i
⊙ Run all modes with PM2
Thanks to this Stack Overflow comment, you can run npm scripts with PM2:
$ pm2 start npm --name "strapi-dev" -- start
$ pm2 start npm --name "strapi-staging" -- run staging
$ pm2 start npm --name "strapi-prod" -- run prod
Also, you need to set up the PM2 Startup Hook in case of reboot:
$ pm2 startup
$ pm2 save
You can check if the startup hook works by rebooting the machine if you'd like to:
$ sudo reboot
$ ssh -i strapi-cms.pem ubuntu@<ec2-public-ip>
$ pm2 ls
/etc/nginx/nginx.conf
with yours
⊙ Replace $ cd ~/strapi-cms
$ sudo mv /etc/nginx/nginx.conf /etc/nginx/nginx.conf.backup
$ sudo cp nginx.conf /etc/nginx/nginx.conf
⊙ Obtain SSL certificates
According to https://nginxconfig.io :
# HTTPS - certbot (before first run): create ACME-challenge common directory
$ sudo mkdir -p /var/www/_letsencrypt && chown www-data /var/www/_letsencrypt
# HTTPS - certbot (before first run): disable SSL directives
$ sudo sed -i -r 's/(listen .*443)/\1;#/g; s/(ssl_(certificate|certificate_key|trusted_certificate) )/#;#\1/g' /etc/nginx/nginx.conf
# Reload Nginx config
$ sudo systemctl reload nginx
# HTTPS - certbot: obtain certificates
$ sudo certbot certonly
--webroot -n --agree-tos --force-renewal \
-w /var/www/_letsencrypt \
--email <your-email> \
-d cms.<yourdomain.com> \
-d staging-cms.<yourdomain.com> \
-d dev-cms.<yourdomain.com>
# HTTPS - certbot (after first run): enable SSL directives
$ sudo sed -i -r 's/#?;#//g' /etc/nginx/nginx.conf
# Reload Nginx config again
$ sudo systemctl reload nginx
All done! Visit https://{cms|staging-cms|dev-cms}.yourdomain.com to see if it works.
⊙ Further optimizations
- See https://strapi.io/documentation/3.x.x/guides/deployment.html
- Remove
strapi-cms/public/index.html
§ Summary
Now you have:
- A Strapi API running on dev & staging & prod mode simultaneously
- PM2-guarded processes with reboot startup hooks
- Forced HTTPS-secured traffic for all
- Auto-renew SSL certificates for free
PRs & issues are welcome! Sharing your experience will save others' time!