Awesome Open Source
Awesome Open Source

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

  1. Click Launch Instance button
  2. Choose AMI: Ubuntu Server 18.04 LTS (HVM), SSD Volume Type
  3. Choose Instance Type: General purpose, t2.micro
  4. Configure Instance: as you like
  5. Add Storage: General Purpose SSD (gp2), 8 GB
  6. Add Tags: as you like
  7. Configure Security Group:
    • SSH (22) - My IP or Anywhere or as you like
    • HTTP (80) - Anywhere
    • HTTPS (443) - Anywhere
  8. Review: click Launch button, then a modal pops up. If you are a:
    • Newbie: Choose Create a new key pair named strapi-cms, download it as strapi-cms.pem
    • Veteran: as you like
  9. Finally, click Launch Instances button


  1. Click Create database button
  2. Select engine: PostgreSQL
  3. Choose use case: as you like
  4. 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
  5. 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
  6. Instance Details panel - Security groups
    • Edit inbound rules: PostgreSQL (5432) - Anywhere
  7. Create databases for dev & staging modes (GUI recommend:
    • Development mode: strapi_dev
    • Staging mode: strapi_staging
    • Production mode: strapi (already exists)

⊙ S3

  1. Click Create bucket button
  2. Name and region
    • Bucket name: as you like
  3. Configure options: as you like
  4. Set permissions
      • [ ] Block new public ACLs and uploading public objects (Recommended)
      • [ ] Remove public access granted through public ACLs (Recommended)
      • [x] Block new public bucket policies (Recommended)
      • [x] 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
  5. 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:
  • Staging mode:
  • Production mode:

§ Warm up EC2

⊙ Login to EC2 & update

Switch to the directory where the key pair strapi-cms.pem locates:

$ ssh -i strapi-cms.pem [email protected]<ec2-public-ip>
$ sudo apt update

⊙ Install Nginx

$ sudo apt install nginx

⊙ Install nvm & Node.js & PM2

  • Install nvm:
    $ curl -o- | bash
  • Install Node.js:
    $ nvm install 10
  • Install PM2:
    $ npm i pm2 -g

⊙ Install Certbot

According to :

$ 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

$ npm i -g [email protected]

Secondary, create a new project (refer to

$ 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

⊙ Complete database configuration

If you prefer best practices like and , 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

$ npm i -S [email protected]

⊙ 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 .
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"

⊙ Add nginx.conf to the project root

If you prefer best practices using /etc/nginx/{sites-available|sites-enabled}, you may need help from or . I prefer single file nginx.conf because of simplicity.

Don't forget to replace all with yours.

⊙ Run

$ cd strapi-cms && strapi start

Your browser will open http://localhost:1337 automatically later.
Create an 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 for further info.

⊙ Configure response & security

Since Strapi will be running behind a well-tuned Nginx, you should:

For more details, please turn to

⊙ Push to a Github free private repo (or Gitlab, etc)

$ 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

⊙ Replace /etc/nginx/nginx.conf with yours

$ 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 - 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.<> \
    -d staging-cms.<> \
    -d dev-cms.<>

# 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} to see if it works.

⊙ Further optimizations

§ Summary

Now you have:

  • A Strapi API running on dev & staging & prod modes 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! tip

Get A Weekly Email With Trending Projects For These Topics
No Spam. Unsubscribe easily at any time.
aws (1,131
graphql (1,095
tutorial (988
postgresql (762
cms (454
https (220
s3 (206
ec2 (57
pm2 (38
rds (27
strapi (19