This content originally appeared on Level Up Coding - Medium and was authored by Zied Ben Tahar
I have been an avid Strapi user for quite some time now. Strapi is an open-source node.js headless CMS, it helps you scaffold an API with its management UI in an easy way and lets you manage content using REST APIs or GraphQL.
The official Strapi documentation for a deployment on AWS is good to get started quick but it is not suitable for repeatable and scalable deployments.
In this article, I will show you how to use CDK in order to deploy a Strapi project on AWS using Serverless components: We will deploy Strapi on ECS on Fargate along with a Postgres Aurora Serverless V2 database.
TL;DR
You will find the complete repo with the complete Github actions workflow here
👉https://github.com/ziedbentahar/strapi-on-aws-with-cdk
1 — Architecture overview
Here the architecture diagram of the components we will be deploying on a AWS account:
In this example, we create a dedicated VPC, but you are not required to do so if you want to deploy Strapi on your own existing VPC.
We have three subnets:
- A public one, where the application load balancer is deployed
- A private subnet, where the ECS cluster is deployed. In this cluster we define the ECS service consisting of the Strapi Task.
- An isolated subnet: The subnet for the database: It does not route trafic to the internet; it does not require a NAT Gateway.
We also create an Aurora V2 Serverless PostgreSQL database as well as a secret containing the database credentials. This secret is then injected securely into the Strapi container.
2 — Setting up Strapi
Before defining the infrastructure, let’s first create a sample Strapi project. Straight from the documentation, creating a new Strapi project is a breeze:
npx create-strapi-app@latest my-awesome-strapi-api
On this step, let’s make sure to select postgres as dbclient and typescript as the preferred language.
Database configuration
The config/database.ts file defines the configuration required to connect to the database. It expects these DATABASE_* environment variables to be defined:
export default ({ env }) => ({
connection: {
client: "postgres",
connection: {
host: env("DATABASE_HOST"),
port: env.int("DATABASE_PORT"),
database: env("DATABASE_NAME"),
user: env("DATABASE_USERNAME"),
password: env("DATABASE_PASSWORD"),
ssl: env.bool("DATABASE_SSL"),
},
},
});
We will configure the ECS Task definition to retrieve the database password from a secret instead of injecting it directly as plaintext environment variable. That way, it cannot be discoverable in the AWS Management Console and to anyone with access to the ECS Task Definition.
The ECS container agent will securely inject DATABASE_PASSWORD env variable into the Strapi container by referencing the secret.
I will describe below how to use CDK to securely inject secrets on a ESC task definition.
Creating the Dockerfile
A Dockerfile defining the container is required to deploy into the ECS cluster. In this example, I will be using the the official Dockerfile provided by Strapi, you can find it following this link
A .dockerignore file is also required in order to ignore these folders while building the image.
node_modules/
.tmp/
.cache/
.git/
build/
3 — Coding the infrastructure with CDK
Let’s first initialize the CDK project. It will live on the same git repo and on the same parent folder as the Strapi projet:
mkdir my-awesome-strapi-project-infra \
&& cd my-awesome-strapi-project-infra \
&& npx aws-cdk init app --l typescript
In order to make our CDK project well organized, each component of our infrastructure will be defined as a nested stack of the Strapi root stack:
On this newly scaffolded project, when creating the stack, we need to set account and region respectively to CDK_DEFAULT_ACCOUNT and CDK_DEFAULT_REGION environment variables. This ensures that the stack is deployed in the account and Region determined by the AWS CDK CLI at the time of synthesis:
Creating the database
As depicted on the diagram above, the database will be deployed on the isolated subnet. A security group is created to allow traffic from the private subnet (where the ECS cluster lives) to the database.
A secret containing the database credentials (username and password) is also created: It will be used when defining the Aurora database credentials and it will be passed to the ECS task in order to be securely injected into the container:
☝️ Note On this example, we use Aurora Serverless V2 with a minimal scaling configuration and one single instance. Depending on your use case, you may want to define a different configuration. The minimum allowed Aurora Serverless V2 capacity is 0.5 ACU.
You can find the database nested stack definition following this link.
Creating the certificate
To secure the application with TLS, a certificate needs to be created for the custom Strapi domain in the AWS Certificate Manager. It will live in the same region as our deployment. This certificate will be then associated to the public load balancer:
As a prerequisite, a hosted zone must be already created. We use HostedZone.fromLookup to retrieve the hosted zone information by name.
This link provides the certificate nested stack definition.
Defining the ECS deployment
We use this CDK L3 solution construct ApplicationLoadBalancedFargateService : As its name implies, this construct represents a solution including an AWS Fargate container service exposed (by default) via a public application load balancer.
Let’s break down the Fargate service definition:
- On the taskImageOptions property, we define the image by using ContainerImage.FromAsset . This CDK built-in helper builds the image automatically from the provided directory, and pushes it automatically to ECR.
- As mentioned above, some values need to be defined as environment variables into the Strapi container: We will create secrets containing values for DATABASE_PASSWORD as well as these tokens JWT_SECRET , APP_KEYS , API_TOKEN_SALT , ADMIN_JWT_SECRET .
- These secrets are then referenced in taskImageOptions.secrets by using ecs_Secret.fromSecretsManager. The ECS container agent will securely inject them into the Strapi container:
We will make sure to update the task execution role policy in order to be able to read the dbSecret and strapiSecret .
☝️ Note: In order to keep this example simple, I have created a single secret StrapiKey that will be used for the required Strapi Tokens.
Limiting the access to the admin UI: I wanted to only authorize some IP addresses to have access to Strapi admin UI and API (routes starting with /admin/*). I add the listener rules on the ALB based on path and inbound source IP:
You will find the complete definition of this deployment here.
Creating the DNS records
This nested stack creates two records A IPV4 and AAAAIPV6 records targeting the ALB:
Putting the whole Stack together
I will use CDK context to pass the applicationName , the hostedZoneDomainName and the authorizedIPsForAdminAccess parameters, as they are available at synthesis time, we can use them in our code.
I will also rely on each nest stack props to pass values from the root stack:
☝️ Following this example, the domain name of the Strapi instance will be https://<your application name>.<your hosted zone domain name>
4— Let’s deploy !
Before deploying the infrastructure: If this is the first CDK application in your AWS account, you will need to execute this command to bootstrap CDK into your account:
cdk bootstrap
Deploying using CDK is pretty straightforward then:
npx cdk deploy --require-approval never \
--context applicationName=<your application name> \
--context hostedZoneDomainName=<your hosted zone domain name> \
--context authorizedIPsForAdminAccess=<comma-separated list of IPs>
You will be able to follow the stack deployment straight from the CloudFormation console
🎉 Once deployed, you will be able to access your Strapi using this public URL https://<your application name>.<your hosted zone domain name>
And your admin UI will be also restricted to only authorized IPs provided in the CDK context of your deployment:
An example of a CI/CD pipline with github actions
👉 You can find following this link a full Github actions pipline that deploys this Strapi stack into an AWS account.
Wrapping up
In this article we have seen how to deploy with CDK Strapi on AWS using Serverless components: Fargate and Aurora Serverless V2. We could go further by improving this architecture: configuring a WAF (web application firewall) and associate it to the load balancer as well as configuring Strapi media library to use a dedicated S3 bucket.
Further readings
- Strapi Developer Docs
- aws-cdk-lib.aws_ecs_patterns module · AWS CDK
- (rds): support for Aurora Serverless V2 · Issue #20197 · aws/aws-cdk
- Tutorial: Specifying sensitive data using Secrets Manager secrets
Deploying Strapi CMS on AWS with ECS on Fargate, Aurora Serverless v2 and CDK was originally published in Level Up Coding on Medium, where people are continuing the conversation by highlighting and responding to this story.
This content originally appeared on Level Up Coding - Medium and was authored by Zied Ben Tahar
Zied Ben Tahar | Sciencx (2022-12-22T03:20:37+00:00) Deploying Strapi CMS on AWS with ECS on Fargate, Aurora Serverless v2 and CDK. Retrieved from https://www.scien.cx/2022/12/22/deploying-strapi-cms-on-aws-with-ecs-on-fargate-aurora-serverless-v2-and-cdk/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.