Introduction

Deploying a robust content management system (CMS) in a scalable cloud environment is crucial for modern web applications. This comprehensive guide will walk you through deploying Strapi, a powerful headless CMS, on Amazon Web Services (AWS). We’ll cover everything from setting up your AWS environment to configuring continuous deployment.

Table of Contents

  1. Prerequisites
  2. Setting Up Your AWS Environment
  3. Launching an EC2 Instance
  4. Setting Up a PostgreSQL Database with RDS
  5. Configuring S3 for File Storage
  6. Preparing Your Strapi Project
  7. Deploying Strapi to EC2
  8. Running Strapi with PM2
  9. Configuring Nginx as a Reverse Proxy
  10. Setting Up Continuous Deployment
  11. Security Best Practices
  12. Conclusion

Prerequisites

Before we begin, ensure you have:

  • An AWS account with administrative access
  • A Strapi project ready for deployment
  • Basic knowledge of Node.js and npm
  • Familiarity with Git and GitHub

Setting Up Your AWS Environment

Creating a VPC

  1. Log in to your AWS Management Console.
  2. Navigate to the VPC dashboard.
  3. Click “Create VPC” and select “VPC and More”.
  4. Configure the following:

  • Name tag: strapi-vpc
  • IPv4 CIDR block: 10.0.0.0/16 (default)
  • Number of Availability Zones (AZs): 2
  • Number of public subnets: 2
  • Number of private subnets: 2
  • NAT gateways: None (to save costs, but consider adding for production)
  • VPC endpoints: S3 Gateway

  1. Enable DNS hostnames and DNS resolution.
  2. Review and create the VPC.

This setup provides a secure network foundation with public subnets for your EC2 instance and private subnets for your database.

Launching an EC2 Instance

  1. Go to the EC2 dashboard and click “Launch Instance”.
  2. Choose Ubuntu Server 22.04 LTS.
  3. Select an instance type (at least t2.small for Strapi).
  4. Configure instance details:

  • Network: Choose your strapi-vpc
  • Subnet: Select a public subnet
  • Auto-assign Public IP: Enable

  1. Add storage (20GB gp2 is usually sufficient).
  2. Configure Security Group:

  • Allow SSH (port 22) from your IP
  • Allow HTTP (80) and HTTPS (443) from anywhere
  • Allow custom TCP on port 1337 (for initial Strapi setup)

  1. Review and launch, creating or selecting an SSH key pair.

Note: Save your key pair file (.pem) securely; you’ll need it to access your instance.

Setting Up a PostgreSQL Database with RDS

  1. Navigate to the RDS dashboard.
  2. Click “Create database”.
  3. Choose PostgreSQL as the engine.
  4. Select “Free tier” for development (choose appropriately for production).
  5. Configure:

  • DB instance identifier: strapi-database
  • Master username: postgres (or your preferred username)
  • Master password: Choose a strong password

  1. Under “Connectivity”:

  • VPC: Choose your strapi-vpc
  • Subnet group: Create new and select your private subnets
  • Public access: No
  • VPC security group: Create new

  1. Additional configuration:

  • Initial database name: strapi

  1. Create database.

Note the endpoint, port, database name, username, and password.

Configuring S3 for File Storage

  1. Go to the S3 dashboard.
  2. Click “Create bucket”.
  3. Name your bucket (e.g., my-strapi-uploads).
  4. Choose the region closest to your users.
  5. Block all public access (for security).
  6. Enable bucket versioning.
  7. Create the bucket.

Preparing Your Strapi Project

On your local development machine:

  1. Install PostgreSQL client:
   npm install pg
  1. Update config/database.js:
   module.exports = ({ env }) => ({
     connection: {
       client: 'postgres',
       connection: {
         host: env('DATABASE_HOST', '127.0.0.1'),
         port: env.int('DATABASE_PORT', 5432),
         database: env('DATABASE_NAME', 'strapi'),
         user: env('DATABASE_USERNAME', ''),
         password: env('DATABASE_PASSWORD', ''),
       },
       useNullAsDefault: true,
     },
   });
  1. Install AWS S3 upload provider:
   npm install @strapi/provider-upload-aws-s3
  1. Configure S3 in config/plugins.js:
   module.exports = ({ env }) => ({
     upload: {
       config: {
         provider: 'aws-s3',
         providerOptions: {
           accessKeyId: env('AWS_ACCESS_KEY_ID'),
           secretAccessKey: env('AWS_ACCESS_SECRET'),
           region: env('AWS_REGION'),
           params: {
             Bucket: env('AWS_BUCKET_NAME'),
           },
         },
       },
     },
   });
  1. Commit these changes to your Git repository.

Deploying Strapi to EC2

  1. SSH into your EC2 instance:
   ssh -i path/to/your-key.pem ubuntu@your-ec2-public-ip
  1. Update and upgrade packages:
   sudo apt update && sudo apt upgrade -y
  1. Install Node.js and npm:
   curl -fsSL https://deb.nodesource.com/setup_14.x | sudo -E bash -
   sudo apt-get install -y nodejs
  1. Clone your Strapi project:
   git clone https://github.com/your-username/your-strapi-project.git
  1. Navigate to your project directory and install dependencies:
   cd your-strapi-project
   npm install
  1. Set up environment variables:
   sudo nano .env

Add the following (replace with your actual values):

   DATABASE_HOST=your-rds-endpoint
   DATABASE_PORT=5432
   DATABASE_NAME=strapi
   DATABASE_USERNAME=your-db-username
   DATABASE_PASSWORD=your-db-password
   AWS_ACCESS_KEY_ID=your-access-key
   AWS_ACCESS_SECRET=your-secret-key
   AWS_REGION=your-s3-region
   AWS_BUCKET_NAME=your-s3-bucket-name

Running Strapi with PM2

  1. Install PM2 globally:
   sudo npm install pm2@latest -g
  1. Create an ecosystem file:
   nano ecosystem.config.js

Add the following content:

   module.exports = {
     apps: [{
       name: 'strapi',
       cwd: '/home/ubuntu/your-strapi-project',
       script: 'npm',
       args: 'start',
       env: {
         NODE_ENV: 'production',
         DATABASE_HOST: 'your-rds-endpoint',
         DATABASE_PORT: '5432',
         DATABASE_NAME: 'strapi',
         DATABASE_USERNAME: 'your-db-username',
         DATABASE_PASSWORD: 'your-db-password',
         AWS_ACCESS_KEY_ID: 'your-access-key',
         AWS_ACCESS_SECRET: 'your-secret-key',
         AWS_REGION: 'your-s3-region',
         AWS_BUCKET_NAME: 'your-s3-bucket-name',
       },
     }],
   };
  1. Start Strapi with PM2:
   pm2 start ecosystem.config.js
  1. Set PM2 to start on system boot:
   pm2 startup systemd

Follow the command it outputs to set up the startup script.

  1. Save the PM2 process list:
   pm2 save

Configuring Nginx as a Reverse Proxy

  1. Install Nginx:
   sudo apt install nginx
  1. Create a new Nginx configuration:
   sudo nano /etc/nginx/sites-available/strapi

Add the following content:

   server {
     listen 80;
     server_name your-domain.com;

     location / {
       proxy_pass http://localhost:1337;
       proxy_http_version 1.1;
       proxy_set_header Upgrade $http_upgrade;
       proxy_set_header Connection 'upgrade';
       proxy_set_header Host $host;
       proxy_cache_bypass $http_upgrade;
     }
   }
  1. Enable the configuration:
   sudo ln -s /etc/nginx/sites-available/strapi /etc/nginx/sites-enabled
  1. Test and restart Nginx:
   sudo nginx -t
   sudo systemctl restart nginx

Setting Up Continuous Deployment

  1. Create a webhook script:
   mkdir ~/NodeWebHooks && cd ~/NodeWebHooks
   nano webhook.js

Add the following content (replace with your values):

   const secret = 'your-github-webhook-secret';
   const repo = '/home/ubuntu/your-strapi-project';

   const http = require('http');
   const crypto = require('crypto');
   const exec = require('child_process').exec;

   const PM2_CMD = 'cd ~ && pm2 startOrRestart ecosystem.config.js';

   http.createServer((req, res) => {
     req.on('data', chunk => {
       const signature = `sha1=${crypto
         .createHmac('sha1', secret)
         .update(chunk.toString())
         .digest('hex')}`;

       if (req.headers['x-hub-signature'] === signature) {
         exec(`cd ${repo} && git pull && npm install && npm run build && ${PM2_CMD}`, (error, stdout, stderr) => {
           if (error) {
             console.error(`exec error: ${error}`);
             return;
           }
           console.log(`stdout: ${stdout}`);
           console.log(`stderr: ${stderr}`);
         });
       }
     });
     res.end();
   }).listen(8080);
  1. Set up the webhook as a system service:
   sudo nano /etc/systemd/system/webhook.service

Add the following content:

   [Unit]
   Description=Github webhook
   After=network.target

   [Service]
   Environment=PATH=/usr/bin:/usr/local/bin
   Type=simple
   User=ubuntu
   ExecStart=/usr/bin/node /home/ubuntu/NodeWebHooks/webhook.js
   Restart=on-failure

   [Install]
   WantedBy=multi-user.target
  1. Enable and start the webhook service:
   sudo systemctl enable webhook.service
   sudo systemctl start webhook
  1. Configure your GitHub repository to send webhook events to http://your-domain.com:8080.

Security Best Practices

  1. Keep your EC2 instance updated:
   sudo apt update && sudo apt upgrade -y
  1. Use AWS IAM roles for EC2 instead of hard-coding AWS credentials.
  2. Regularly rotate database and AWS access credentials.
  3. Enable and configure AWS Web Application Firewall (WAF) for additional security.
  4. Implement HTTPS using Let’s Encrypt and Certbot.
  5. Regularly backup your database and Strapi uploads.

Conclusion

This comprehensive guide has walked you through deploying Strapi on AWS, from setting up your network infrastructure to configuring continuous deployment. By following these steps, you’ve created a scalable, secure, and efficient content management system.

Remember to regularly update your Strapi installation, Node.js version, and system packages to ensure you have the latest security patches and features.

As your project grows, consider implementing additional AWS services like Elastic Load Balancer for high availability, CloudFront for content delivery, and CloudWatch for monitoring and logging.

Happy building with Strapi and AWS!

Hanzala — Software Developer🎓

Thank you for reading until the end. Before you go: