Deploying Strapi to AWS with AppPack

Strapi is an open-source headless CMS built on Node.js.

It provides a simple local setup which we can build on to take it from development-ready to production-ready and scalable on AWS.

The code for this post lives in ipmb/strapi-demo on GitHub.

note: parts of the screencast are sped up for brevity.

Local Set Up

We'll assume your local machine is prepped to run Node.js projects. To get started we can follow their install instructions:

npx create-strapi-app@latest strapi-demo --quickstart

This generates the boilerplate code and gives you what you need to start working locally. You can follow the rest of their instructions to run the site locally, but we'll skip ahead to the steps we need to make it production-ready.

Strapi works with both yarn and npm. We'll be using yarn for this demo, so we can prep the repo by running:

rm package-lock.json
yarn lock

Connecting to the database

Strapi defaults to using a local SQLite database. Since we'll be running it in ephemeral containers, we'll connect to a Postgres server running on AWS. AppPack will provide the app an environment variable named DATABASE_URL with the database connection details.

We need to:

  1. Modify the production database config to parse the DATABASE_URL into connection details
  2. Run yarn add pg pg-connection-string
diff --git a/config/env/production/database.js b/config/env/production/database.js
new file mode 100644
index 0000000..1687684
--- /dev/null
+++ b/config/env/production/database.js
@@ -0,0 +1,18 @@
+const parse = require('pg-connection-string').parse;
+const database = parse(process.env.DATABASE_URL);
+module.exports = ({ env }) => ({
+ connection: {
+ client: 'postgres',
+ connection: {
+ host: database.host,
+ port: database.port,
+ database: database.database,
+ user: database.user,
+ password: database.password,
+ ssl: {
+ rejectUnauthorized: false
+ },
+ },
+ debug: false,
+ },
+});

Storing file uploads in S3

AppPack deploys apps as containers to ECS, so the filesystem is ephemeral. Any non-temporary files generated by the application need to get stored on S3. AppPack supports both private buckets (which require generating a signed URL for file access) and public buckets (where files are directly accessible on the internet by default). For this application, we're going to assume that all uploads are public and use the public S3 bucket add-on. To do that, we need to:

  1. Modify the production plugins config to setup the aws-s3 plugin
  2. Modify the middleware config to allow viewing files in the S3 bucket in the admin site
  3. Run yarn add @strapi/provider-upload-aws-s3

Note: AWS credentials will be available to the app automatically -- no need to manually define an access/secret key.

diff --git a/config/env/production/plugins.js b/config/env/production/plugins.js
new file mode 100644
index 0000000..e6b97c6
--- /dev/null
+++ b/config/env/production/plugins.js
@@ -0,0 +1,19 @@
+module.exports = ({ env }) => ({
+ upload: {
+ config: {
+ provider: 'aws-s3',
+ providerOptions: {
+ region: env('AWS_REGION', 'us-east-1'),
+ params: {
+ Bucket: env('PUBLIC_S3_BUCKET_NAME'),
+ },
+ },
+ actionOptions: {
+ upload: {},
+ uploadStream: {},
+ delete: {},
+ },
+ },
+ },
+ // ...
+});
diff --git a/config/middlewares.js b/config/middlewares.js
index 04a9aa9..ed51e61 100644
--- a/config/middlewares.js
+++ b/config/middlewares.js
@@ -1,4 +1,29 @@
-module.exports = [
+module.exports = ({ env }) => [
'strapi::errors',
- 'strapi::security',
+ {
+ name: 'strapi::security',
+ config: {
+ contentSecurityPolicy: {
+ useDefaults: true,
+ directives: {
+ 'connect-src': ["'self'", 'https:'],
+ 'img-src': [
+ "'self'",
+ 'data:',
+ 'blob:',
+ 'dl.airtable.com',
+ `${env('PUBLIC_S3_BUCKET_NAME')}.s3.${env('AWS_REGION', 'us-east-1')}.amazonaws.com`,
+ ],
+ 'media-src': [
+ "'self'",
+ 'data:',
+ 'blob:',
+ 'dl.airtable.com',
+ `${env('PUBLIC_S3_BUCKET_NAME')}.s3.${env('AWS_REGION', 'us-east-1')}.amazonaws.com`,
+ ],
+ upgradeInsecureRequests: null,
+ },
+ },
+ },
+ },
'strapi::cors',

Ready to deploy to AWS?

AppPack is the easiest way to deploy your apps to AWS.

Homer on a treadmill on a chair looking at a computer

AppPack deployment

Prior to creating your application, you'll need to complete the Initial Setup tutorial and create a Postgres database in your cluster.

You can view a screencast of the entire app creation process above or continue reading for additional details.

To create the application, we'll run:

apppack create app strapi

Be sure to:

  • Set the healthcheck endpoint to /
  • Enable the database add-on
  • Enable the public S3 bucket add-on

Once the app is created, we'll set a few config variables the app is expecting:

apppack -a strapi config set NODE_ENV=production
apppack -a strapi config set AWS_REGION=us-east-2 # replace with your AWS region
apppack -a strapi config set ADMIN_JWT_SECRET=your-admin-jwt-secret # replace with random string
apppack -a strapi config set API_TOKEN_SALT=your-api-token-salt # replace with random string
apppack -a strapi config set APP_KEYS=your-first-app-key,your-second-app-key # replace with two random strings
apppack -a strapi config set JWT_SECRET=your-jwt-secret # replace with two random strings

Then we can kick off the first build (future builds will happen automatically on pushes to the repo):

apppack -a strapi build start --watch

You can also view your build in the web interface:

screenshot of apppack build pipeline

Finally, we're ready to start working with our app. To open it in your default browser, run:

apppack -a strapi open

And there you have it. Your Strapi site is live. With AppPack, there are no servers to manage or maintain. Everything runs in your AWS account using AWS-native managed services.