Building Performant, Scalable, Fully Automated, and Cost-Efficient Web Infrastructure for 2021 (Part 1)

Photo by Aleksejs Bergmanis on Pexels


Throughout this series we’ll be setting up the building blocks of what I would consider to be a very robust web development infrastructure. There will be lots of optimisations to be made within each of the technology choices I make but the deeper you go, the more opinionated it becomes. Consider what we build here to be the foundations of a performant, scalable and cost efficient web architecture. Where you take it from there is entirely in your hands. With that in mind, let’s take a look at the technology stack we’ll be using throughout the series:

  • Next.js: Production ready React framework with hybrid static & server rendering, smart bundling, route pre-fetching, and much more.
  • Strapi: 100% Javascript, fully customisable, developer-first headless content management system.
  • Terraform: Open-source infrastructure as code software tool that provides a consistent workflow to manage hundreds of cloud services.
  • Docker: Provides the ability to package and run applications in a loosely isolated environment called a container.
  • Google Cloud Run: Deploy highly scalable containerised applications on a fully managed serverless platform.
  • Google Cloud SQL: Fully managed relational database service for MySQL, PostgreSQL, and SQL Server.

In the first part of this series we’ll be looking at Bitbucket, Strapi, Next.js and Docker Compose. Let’s get started!


Let’s get started by setting up the repo where our project will live. We’ll be using Bitbucket since that’s what I use for professional work but this should work with any other service like Github or Gitlab. For the purpose of this article though I recommend using Bitbucket as pipelines configuration differs between services.

Once you’re logged into Bitbucket, go ahead and create a new repository. I named mine Next Strapi Cloud Run but you can name yours whatever you like. After that, clone the repo to your local machine.

That’s our repo all setup, now we can move on to the fun stuff.


Strapi is a powerful open source headless CMS with GraphQL functionality built in. I have chosen to use Strapi for its simplicity and flexibility. It also has pretty detailed documentation and probably the nicest UI of all the open source CMS’s I’ve seen so far (imo 😅).

Before we install Strapi we need to get a mySQL database running on our machine. Thankfully this is pretty trivial with Docker Compose. Make sure you have Docker installed and running on your machine first then go ahead and create a file named docker-compose.yml at the root of your project and populate with the following:

Here we are just pulling in mySQL version 5.7, setting the credentials for the database and finally mapping the host and container ports. In your terminal navigate to the repo we setup earlier and run:

docker-compose up

You should see similar to the following:

Now we’re ready to install Strapi!

Stop mySQL and Adminer by pressing ctrl + c then run the following:

npx create-strapi-app cms --quickstart

This will create a new directory cms, install all the required dependencies, scaffold a basic Strapi app for us and run the application. Once that’s complete go ahead and stop Strapi running in your terminal.

We now need to make a few updates to get our mySQL database working with Strapi. In your editor, navigate to cms/config/environments/development, open database.json and update it with the following JSON:

Let’s update our docker-compose.yml file to include our cms so we can pass these environment variables to our application:

Strapi has a dedicated docker image for running Strapi so that’s what we’ll be using as our image. The rest is all pretty self explanatory: Set the working directory, run Strapi and expose port 1337 (but not before mysql is ready), set our volumes and environment variables. Next we need to install mySQL:

cd cms && yarn add mysql

Finally lets enable GraphQL by running strapi install graphql. We’ll be using this to pull content into our Next.js app later on.

Run docker-compose up again and we should now see our cms running on localhost:1337 with the database all hooked up. Play around to make sure everything is working.

Create a new Single Type by clicking on the Content-Types Builder cta in the menu. Set the display name as test, add a short text field named title and click save. Now click the newly created Test type that appears in the menu and set the title to Hello Next App and save. Now click Roles & Permissions in the left menu. You should see the Test type with some checkboxes near the bottom right. Check find and click save. Now we can move on to our next piece of the puzzle.


I think most, if not all web developers will have heard of Next.js by now. If you haven’t I strongly recommend you check it out, it’s incredibly powerful. Here is the quote from their homepage:

“Next.js gives you the best developer experience with all the features you need for production: hybrid static & server rendering, TypeScript support, smart bundling, route pre-fetching, and more. No config needed.”

It’s very easy to setup so let’s go ahead and get started. In your terminal navigate to the repo we setup earlier and run the following command:

npx create-next-app app

This will scaffold a fresh Next.js app inside a newly created directory named app. Let’s update our docker-compose.yml file to include the Next.js app:

Very similar approach to Strapi but here we also add the .next folder to our volumes and expose the Strapi GraphQL URL as an environment variable. If you run docker-compose up again, you should now see our Next.js app running on localhost:3000.

Great! Now that we have a simple app running, let’s connect our cms to Next.js. Create a next.config.js file at the root of our app directory where Next.js lives and add the following code:

All this does is expose the CMS_GRAPHQL_URL environment variable to the frontend of our Next.js app. Now that we have access to the cms api we need to make some updates to our app so we can pull in the content. First lets install graphql and a library to help us make our GraphQL requests. I will be using graphql-request. It’s lightweight and easy to use. Navigate to your app directory in terminal and run the following:

yarn add graphql graphql-request

Then open app/pages/index.js in your editor and add the following code to the top of your file:

First we are importing two functions from graphql-request: request and gql. Two simple abstractions that will make our HTTP requests much more succinct and understandable.

Next we export our getStaticProps function. Inside the function we build our GraphQL query, execute the request and return our props object. Notice the revalidate property. This is one of Next.js’ new and really awesome features: Incremental Static Regeneration. I won’t go into the details here but if you’re not familiar with the concept then take a moment to read the link I’ve just shared. It’s a really simple but powerful concept.

Finally don’t forget to bring the test prop into your actual component and replace the h1 content:

Welcome to <a href="">Next.js!</a>

with the following:


Let’s run docker-compose up again to check everything is working. If all goes well you should be greeted with a screen like this on localhost:3000:


We’re all done setting up locally now. Push all your updates to Bitbucket and grab a coffee, beer, whiskey or whatever your poison is! There was a lot of information to take in so if anything isn’t clear leave a comment and I’ll respond ASAP! I really hope you enjoyed following along.

In the next article we’ll be configuring Terraform to help us provision all the infrastructure we need for our applications to run on the web. See you there and thanks for reading!

Click here for part 2

Software engineering manager with a keen focus on web technologies.