Using Ansible For Automatic Deployment Of A Strapi CMS To Amazon AWS

Frank Haubenschild
8 min readMay 7, 2021

The headless Content Management System Strapi gets more and more popular — by now already more than 36k stars on Github underline its popularity. With the presented Ansible solution you can automatically spin up the needed AWS cloud infrastructure (VPC, RDS, EC2, S3) and place a configured Strapi project into it.

Traditional Vs Headless Content Management Systems

If you want to present your content like a blog, your photo portfolio, or any kind of digital content to the public you will need a Content Management System. Basically, there are two major kinds of CMS:

1. Traditional/Monolithic CMS
This type is bundling a backend and frontend part. In the backend, you as an author are writing your content. The frontend part consists of the generated web pages presenting your content in a nice shiny-looking way to your audience. Joomla and WordPress are probably the most frequently used CMSs in this area.

2. Headless CMS
In comparison to traditional CMS, a headless CMS does not offer you any kind of a frontend. Only a backend is available. A headless CMS offers an API that can be used by a frontend application to query the data and to generate the corresponding web pages.

Strapi — The Headless CMS

Strapi is a Github-based open source project [3] under the MIT Expat License. With more than 226k downloads per month, a big feature set, great documentation, its own slack channel, and a huge amount of video tutorials it is getting more and more popular. Strapi is a great candidate if you want to have all the flexibility for dealing with your content. For accessing your content Strapi offers a traditional REST- but also a modern GraphQL-API.

The Strapi backend.

Ansible For Automating Your DevOp Work

The open-source project Ansible [1] is for sure one of the most frequently used tools for automating tasks like installing and configuring software on a remote Linux machine. It also supports the provisioning of cloud infrastructure like compute instances or database systems. With more than 3000 modules that are extending its use for all thinkable areas, it is the DevOps swiss army knife.

Ansible — a GPL based Automation System maintained by Red Hat

This article does not explain all the basics of Ansible. There are plenty of good tutorials out there. The Getting Started page from the Ansible project is a good source to start learning the basics. Using Ansible is not rocket science and you should be ready to run and write your first Ansible scripts quite fast.

Ansible differentiates between a control host and target host(s). The system where Ansible runs and is executing the so-called playbooks and roles is called control host. The system which gets provisioned or where the software will be installed/configured on is called the target host. A target host can just be one system or even a hundred or thousands of servers. The control and target host are communicating via ssh. On the target host, only python must be installed.

Installing Ansible is quite easy and on an Ubuntu-based control host you are ready to go by just typing:

sudo apt install ansible 

Ansible runs playbooks which are basically a list of commands which have to be executed (configuration, installation, copy or modify files, provisioning of cloud infrastructure, etc.) on the target host(s). Good practice in the Ansible world is to create roles — which are commands for just one particular task — i.e. a specific role to set up an AWS RDS Database and another role to spin up a Virtual Private Cloud (VPC). Roles are really helpful to structure your Ansible project.

Deploy Your Strapi CMS on Amazon AWS

If you want to install Strapi locally on your development machine you only need Node.Js to be installed. You then can create the Strapi application with:

npx create-strapi-app my-project --quickstart

If the build process went through you will reach the Strapi Admin-Panel under the following URL:

http://localhost:1337/admin

That’s it. Under a minute and you are ready to go.

Press fast forward and imagine you are now ready to bring your content, your web page, or your next unicorn startup online. You will need a cloud-based solution. A server where Strapi is running and a database system for the data is a minimum. The Strapi project offers great tutorials on how to bring Strapi online on nearly all big cloud providers. Microsoft Azure, Heroku, DigitalOcean, and Amazon AWS just to name the major cloud providers.

All these tutorials have one thing in common — you manually have to go through a long list of steps to get it running in the cloud. For Amazon AWS these are the major steps :

  1. Launch an EC2 virtual machine
  2. Configure and install a database (i.e. PostgreSQL on RDS)
  3. Configure S3 as an upload destination
  4. Configure the EC2 instance as a Node.js server
  5. Create and configure a Strapi application to the server
  6. Install and configure PM2 Runtime to automatically launch Strapi

Let Ansible Do The Job For You

If you are looking for an automatic way to set up all this, you can use my Ansible solution hauben/ansible-play-strapi [9] which I put on Github. With this Ansible solution, you can just run the playbooks with its roles, lay back, drink a cup of coffee, and watch how your backend gets automatically provisioned for you.

The following diagram shows how the AWS deployment will be created. An Ansible role VPC will spin up a VPC and create private and public subnets. The role RDS creates a PostgreSQL instance on RDS. This database instance will be placed into a subnet group of three private subnets allowing failover switches into another region in case of data center fail-outs. An EC2 instance where Strapi will be installed gets created and placed in a public subnet. This public subnet will be accessible from the internet. Because the database must only be accessible from the EC2 instance and not from the outside world it is good practice [8] to have it decoupled from the internet.

Strapi AWS Deployment

Allow Ansible To Connect To Your AWS Account

The Ansible roles VPC, RDS, EC2, and S3 need programmatic access to your AWS account. To do so generate a new IAM User and attach the corresponding policies. You may not need the FullAccess types — feel free to limit the corresponding rights by shrinking them down to a minimum. If the user is not needed anymore, delete it or just make the security key inactive.

Ansible needs the access key ID and the secret access key to connect to an AWS account.
Attach policies to allow the creation of the VPC, your instances (RDS, EC2), and the S3 bucket.
Finally, write down the generated Access key ID and the corresponding Secret access key.

To give the Ansible role EC2 access to the EC2 instance via ssh you can create a key pair which later gets referenced within our Ansible role.

Using a key pair to connect the Ansible control host via ssh to the target EC2 instance.

Configure The Ansible Variables

The Ansible solution has the following structure:

ansible-play-strapi/
├─ inventories/
│ ├─ development/
│ │ ├─ group_vars/
│ │ │ ├─ all/
│ │ │ │ ├─ credentials.yml
│ │ │ │ ├─ main.yml
│ │ ├─ production/
│ │ ├─ staging/
├─ playbooks/
│ ├─ aws_provisioning.yml
│ ├─ strapi.yml
├─ roles/
│ ├─ ec2/
│ ├─ rds/
│ ├─ s3/
│ ├─ strapi/
│ ├─ vpc/
├─ ansible.cfg
├─ site.yml

You may want to configure the following variables first before running the play:

  1. ./ansible.cfg:
    - Specify which inventory you want to use (development, staging, or production)
    - Point to the private key file generated above
  2. ./inventories/<stage>/group_vars/all/credentials.yml:
    Specify the AWS credentials for the RDS database (username and password) and the generated access key and the corresponding secret. If you clone the Github repo, these variables are empty. If you then run the playbooks you will get an error saying that you have to specify these variables first.
  3. ./inventories/<stage>/group_vars/all/main.yml:
    Global variables for AWS which are used roles-wide like region, database settings, and S3 bucket name
  4. ./roles/<ec2|rds|s3|strapi|vpc>/defaults/main.yml:
    Each role has its own configuration. Check this variable definition if they fit your needs and change them if needed.

Secure Your Credentials Using Ansible-vault (optional but recommended)

The AWS credentials access key/access secret and the RDS database username and password are stored as mentioned in ./inventories/<stage>/group_vars/all/credentials.yml. It is strongly recommended to create an Ansible vault and encrypt this file instead of having the credentials in cleartext laying around. Below you can see an encrypted and a decrypted version of the credentials.yml file.

Decrypted version:

username: rds_db_admin
password: <your-secure-db-password>
aws_access_key: <your-access-key-id>
aws_secret_key: <your-secret-key>

Encrypted version:

$ANSIBLE_VAULT;1.1;AES256326134386238666438613061313161306262303331336362613739623565303066323432376432613736343166383161326133353937363738640a326533303564386461376534383833623665396431626564396631656664613539663364323533303634656162363331316362320a643263666634323465373635666463666430373534646633363630316666396662386530376237376334353935656139333765303266346134633664376331653864383363653031316136653966626236666534663361376238623765356132363166613234646266653565616564646364653438333330613730666561383962616662366238326161386332313231346236623134663339643132643430386239663130306261653430303232363332353033323664313832376266633366656366653632356565396630363036323037303335303835376534613034303764386332643438373032326266643930343839646662

Finally, Run The Play

If you have followed the above steps you can run the play with the following command:

./site.yml --ask-vault-pass

If the whole deployment went through successfully (takes up to 10–13 minutes) open your browser and open the public IP address of your EC2 instance and go to the configured port (default: 1337). You should see the following screen and you can create your first user — the administrator.

You will reach the strapi application backend via http://<public-ec2-ip-address>:1337

Notes for the Strapi role:
The configured strapi application gets served from the Ansible role strapi via a Jinja2 template (roles/strapi/templates/env.j2). Strapis configuration will be stored in the hidden .env file. This could be further improved to have a .env file for each stage.

DATABASE_HOST={{ hostvars[‘localhost’].rds.endpoint.address }}
DATABASE_PORT=”{{ db_port }}”
DATABASE_NAME=”{{ db_name }}”
DATABASE_SSL=”{{ db_ssl_enabled}}”
DATABASE_USERNAME=”{{ username }}”
DATABASE_PASSWORD=”{{ password }}”
AWS_ACCESS_KEY_ID=”{{ aws_access_key}}”
AWS_ACCESS_SECRET=”{{ aws_secret_key }}”
UPLOAD_S3_BUCKET_REGION=”{{ s3_region }}”
UPLOAD_S3_BUCKET=”{{ s3_bucket_name }}”
STRAPI_HOST=”{{ strapi_host }}”
STRAPI_PORT=”{{ strapi_port }}”
ADMIN_JWT_SECRET=”{{ token }}”

Strapi is also prepared to use the configured S3 bucket as an upload destination. Therefore the AWS S3 upload provider plugin was installed.

Notes for the EC2 role:
The EC2 security group has an open port 22 for ssh. This is only for development purposes and should never exist in that way on a production system. Adding an nginx proxy to the configuration and removing port 1337 should also be considered a MUST on any production system.

Summary

The presented Ansible solution hauben/ansible-play-strapi for automatic deployment of Strapi on AWS is for sure not perfect. If you have ideas, comments, or suggestions on how to improve it please do not hesitate to write issues on Github or contribute in any way you like. If you like it, share this article and give your thumbs up.

References

  1. [Ansible] https://www.ansible.com
  2. [Strapi.io] https://strapi.io
  3. [Strapi Github] https://github.com/strapi/strapi
  4. [Ansible Module Community AWS] https://docs.ansible.com/ansible/latest/collections/community/aws/index.html
  5. [Ansible Module Amazon AWS Collection] https://docs.ansible.com/ansible/latest/collections/amazon/aws/index.html#plugins-in-amazon-aws
  6. [ansible-modules-pm2] https://pypi.org/project/ansible-modules-pm2/
  7. [MIT Expat License] https://en.wikipedia.org/wiki/MIT_License
  8. [Tutorial: Create an Amazon VPC for use with a DB instance] https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_Tutorials.WebServerDB.CreateVPC.html
  9. [Ansible Play To Deploy Strapi on AWS] https://github.com/hauben/ansible-play-strapi

--

--

Frank Haubenschild

Dad, Software Engineer, Photographer, Reef- & Bee-Keeper, Founder, Drone Pilot — 🤓 💻 📷 🐝 🐠 💡👨‍✈️