This article is short, but I gave extensive details and illustrations so that the process will be easier to apply, meaning you don’t need more than 3 cups of coffee to finish these steps :)

Introduction

Software architecture is a good field to be interested in since it gives you the inner knowledge on how to design the systems you want, as part of this article we give extensive details that lead to the construction of NumericaIdeas’s blog, yes the blog of our dream. It’s a piece of components from the open-source community with a clean and nice architecture that we can scale and improve at will. Let’s deep dive into the next lines while sincerely hoping it’ll help you somehow.

By following this article, you’ll be able to set up the same blog, therefore we’ll make efforts to provide guides and automated scripts to make the process easier for you to sync the components involved. After this, only your imagination could limit you to customize it as you need still by benefiting from high-speed page rendering and great performance.

If you missed the previous article that explains why we opted to Ghost instead of WordPress you should definitely check it out to have a better understanding of the inner thoughts that led to this architecture plan. That said, we'll only focus on the building/deployment parts while explaining the role of each component.

High Level Design

The whole system is a linkage of many components with each of them having a single purpose or feature, here's the architecture diagram that explains our blog deployment strategy plus the way it gets updated during its lifecycle:

First, we are using the open-source Ghost CMS as a headless backend which implies that our front-end is decoupled from the backend. The Ghost system here holds our CMS data which contains articles, authors, users, subscribers details saved in a MySQL database in a fully managed service called AWS RDS. The backend has the capability to push some emails via Mailgun to our members whenever new articles are published. Assets (images, videos, and so on) uploaded while writing articles are being saved in an AWS S3 bucket served via a CloudFront distribution linked with a custom subdomain under HTTPS protocol (ACM public certificate).

Second, as per the diagram you can clearly identify that Vercel platform is being used for the front-end deployment, it’s taking the most updated version of our source code from our Git repository. Therefore, whenever there are new commits made to the master and develop branches it’s respectively going to build and deploy the production and development versions of our site right away. A recent improvement has been to enable Slack notification into our Workplace so that we know every time SUCCEED or FAILED deployments happened. To clearly state it, Vercel is being used here for static page generation and also as a CI/CD tool since our App is updated as we have new commits on the GitLab side and on CMS data changes from the backend part (dashboard managed by administrators and writers).

In the end, the visitors will be able to load statically generated pages located in Vercel and CloudFront CDNs so designed to render speedily and smoothly.

Frontend App Served via Vercel + GitLab/GitHub

To install the frontend (site), you have to copy this NextJS template either by a clone-and-push or via a fork operation to your own remote repository platform then you should create a Vercel account using that same platform where your repository is located at. At the moment of writing this article, Vercel does support GitHub, GitLab, and Bitbucket. The CI configurations are being done for you by Vercel (whose you provided the right to your repository) plus a tiny WebHooks.

You are free to use whatever Git collaborative platform you want. The only important step is to link the repository you have there at the deployment step on the Vercel side. In the following lines, you have some screenshots that show how to.

First, log in to Vercel using your repository platform (for us, it’s GitLab). You should see a simple UI with a single click action to perform:

Next, if you aren't prompted to, you have to hit the button “New Project” to create a new project, it’s located on the top right of the interface.

Next, you simply have to pick up the Git provider (button “Continue with Provider”) where your project (source code) template is hosted at then the next screen will show all your repositories for you to choose one to grant permissions to Vercel on.

For the sake of the demonstration, we have used a copy of that template which is a simple fork, while our blog source code is hosted on GitLab. Once you are sure about the repository choice, let's click on the Import button.

Finally, once you have defined your project name, you can simply click on the deploy button following the next screen after project import. Let’s keep the default options, it looks like this:

A few minutes later you have a blog fully deployed and accessible worldwide with a link similar to next-cms-ghost-five-mocha.vercel.app.

The Congratulations page is the assurance that everything is properly deployed. By clicking on the preview image on the left, you can open your new blog (you can rejoice before moving to the next steps), an additional option is available to access its Dashboard by following the link on the right side.

By opening the site, you see the default content which has been left for demonstration purposes only, just browse around to feel the nice, clear, and evident experience of the pages:

In case you aren't OK with the sub-domain generated, you are free to change that from your App dashboard. Let’s open the View Domains page, you can see that button down to the Visit one:

Over there, you have to fill in the domain (or sub) names to link with your blog. An error message will recommend you update your DNS CNAME records accordingly. Here’s an illustration to have a clear overview:

Our setup includes a specific subdomain for our development installation and the root one for the production version of the blog, each of them running on different branches respectively develop and master. Vercel will automatically rebuild our App on new commits made on these branches:

At this stage, you should already have a sample blog printing some articles included by default in a fresh version of the template. The next parts will be about setting up the backend (Ghost installation) on Heroku so that whenever you update, either your articles or the CMS data your own changes will be displayed on the website.

Le’s note that the same domain name can’t be used by many Vercel accounts at the same time.

CDK Creation of IAM user, S3 Bucket and MySQL Database

Three more resources are needed to implement the same installation that we have for the NumericaIdeas blog:

  • S3 bucket: that’s where our assets will be stored whenever we perform uploads on the backend part (CMS).
  • IAM user: user to provide to Ghost CMS with read and write permissions on the S3 bucket.
  • MySQL database: the database management system to store our CMS data (articles, authors, users and CMS settings).

This part is more about using a tiny CDK application that gets you ready with, an IAM user, an S3 bucket, and a MySQL (RDS) database created on AWS. We made this automation for hassle-free provisioning of resources so that you can move to the next part smoothly.

Simply install and configure the AWS-CLI while making sure to define the right region, AWS Access Key ID, and AWS Secret Access Key with the required permissions to run the CDK App. For the sake of simplicity, we used an admin account to run the automation script but we deleted it right after the deploy command. This article isn't about IAM itself so you may need to take into consideration the least privilege best practice if you aren't going to delete that admin user at the end of this section.

In addition to CLI configurations, you have to define a few environment variables needed by CDK:

  • CDK_DEFAULT_ACCOUNT: which represents your AWS account id without the dash (-) in it.
  • CDK_DEFAULT_REGION: the default region where you would like the resources to be created/provisioned.

Once the CLI is well-configured, make sure you have NodeJS installed on your computer, then run the following commands in order:

  • git clone https://github.com/numerica-ideas/CDKGhostAutomation.git cdk-automation: To get the source code from the repository.
  • cd cdk-automation: jump into that folder from the command line.
  • npm install: install the project dependencies.
  • npm run cdk doctor: make sure no errors are printed in your terminal.
  • npm run cdk deploy: to deploy the stack which creates the bucket, user and database for you.
  • npm run cdk destroy: A destroy command is also available whenever you want to erase the provisioned resources (optional). Don’t do this if you are still following the article to have your blog properly working.

In case you would like to have your proper identification and options while provisioning your resources, you have to redefine some of the CDK APP variables (Environment Variables and DB Credentials) present in the lib/config.ts file as illustrated in the following screenshot (optional):

That's all, the Outputs of the deploy command gives you extensive details that you should save for further use into CMS and Vercel as environment variables:

By using MySQL Workbench (or any alternative) you should be able to connect to your DB instance by simply testing the connectivity as follow:

Note: CDK version in use is 1.143.0, it's very important for the current implementation of the automation App.

CDN Distribution of Static Assets (Optional)

This part is optional but is surely gives nice performance adjustments since it improves the speed of your blog by caching the static assets (images, videos … etc) close to your visitors locations by using edges locations in a CDN network.

As part of this article, two options are available for you. Which are either by using Vercel CDN (default and recommended) via Next Image component or by configuring an AWS CloudFront distribution using a custom domain name under HTTPS protocol.

Next Image Component

This is the quickest and most popular approach while deploying on Vercel since it handles everything for you and you don’t have to configure anything elsewhere, in our case, it simply involves enabling the following environment variables while deploying your App on Vercel, and you are DONE:

  • JAMIFY_NEXT_FEATURE_IMAGES=true
  • JAMIFY_NEXT_INLINE_IMAGES=true
  • JAMIFY_NEXT_SOURCE_IMAGES=true

Still, on the View Domains page, let’s click on the Environment Variables menu item on the left panel of the page to access a page similar to the following:

From that page you can define all the variables needed to statically build your App, Vercel uses these privately to generate the static HTML pages derived from the source code you provided. It’s important to choose the environment that the variables should be used on, it matches with what you have defined for the domain names (dev and prod).

The only disadvantage of this method (maybe a personal preference) is that it doesn’t use your website/blog domain name to serve your assets via their CDN, which is what they provide for now by doing some interesting optimizations server-side and pushing your content into their own CDN linked with their domain names. I’m not sure if it's possible or not to customize this behavior.

Setup a CloudFront Distribution with a Custom Domain

In case you want to serve your assets via a CDN using your domain name (not Vercel one), you still have the option to create an AWS CloudFront distribution.

The CDK App provided for you in the previous part can create a CloudFront distribution out of the box, you simply have to enable that option in the config file (enabledCDN) prior to running “npm run cdk destroy" again.

Once redeployed, it creates a CloudFront distribution and prints out the distribution link but you have to follow this article to link it with your custom domain name (ours is assets.numericaideas.com). If you aren’t using AWS Route53 to manage your domain name, you will be creating the corresponding CNAME record on your domain registrar dashboard to point to the distribution’s alternate domain.

If done correctly, you need to provide the environment variable S3_ASSET_HOST_URL to the Ghost backend so that it knows the domain name to use for uploaded files. Read the next section to find out how to provide that URL to the backend deployment.

Ghost CMS Deployment

Ghost Pro

It’s still an option to buy a Ghost pro subscription directly from the creators, a fully managed solution available as a SAAS product. Once bought, you’ll have a full dashboard where you can manage your users and your data with ease.

One-click Deployment

A Heroku one-click install of Ghost version 3 is available as part of this free software created by Coby Chapple and available over on GitHub. By clicking on the pink button, it’s going to automatically deploy it for you so that later you can simply update some sensitive information (environment variables) in order to enable additional features that we are going to cover just down in the following part.

Linking Ghost Backend with Bucket and Database

For your Ghost backend to save assets on S3, you have to provide valid user credentials (access key id & secret access key) with the permission to manage data in the specified bucket created using the CDK automated App.

Since we opted for the one-click deployment, we can open our Heroku App, then click on the Settings tab, and lastly press the Reveal Config Vars to manage the Environment Variables.

Once that section opened, we have to provide the following variables by using the Outputs of the CDK App that we ran on the top, only the public URL is to be taken from our Vercel domains configurations:

  • MYSQL_DATABASE_URL
  • S3_BUCKET_NAME
  • S3_BUCKET_REGION
  • S3_ASSET_HOST_URL (In case you are using AWS CloudFront as CDN instead of Vercel).
  • S3_ACCESS_KEY_ID
  • S3_ACCESS_SECRET_KEY
  • PUBLIC_URL: The url of the front-end deployed via Vercel.

Once applied, the App should auto-restart to take into consideration your changes. In case it isn’t, you can restart the Heroku App dynos from within the App dashboard:

Enable App Deployment on CMS Data Change

The Continuous Deployment aspect of the setup has two aspects. The first one is via the Git repository where each commits triggers the App deployment and the other is through the CMS data changes WebHook which is covered by this section.

Each time there are changes on the CMS side, either concerning articles, App settings, and others, the static generation should run to reflect these changes on the UI if needed.

This is done by creating a Ghost WebHooks for data changes and providing a  Vercel Deploy Hooks link so that the deployment link will be called each time changes occurred.

Let’s first go to our Vercel App’s Settings page, follow the Git tab, then just down to the “Deploy Hooks” section, we have the possibility can create our hook by providing a name and hitting the Create Hook button to reach a page similar to the following:

Let's copy the generated link since we’ll use it on Ghost side.

Now from Ghost admin panel, let’s jump to the Settings section in the menu on the left. From there, you can create a Custom Integration using Site Changed (rebuild) as Event, and the URL should be the Vercel Deploy Hook created before.

As of now, all four platforms (Ghost/Heroku, GitHub/GitLab, Vercel, and AWS) are synchronized for App builds/deployments as changes are performed either by authors (articles, settings & profile) or developers (new commits).

Mailgun Setup

Providing email sending capability to your Ghost backend is a straightforward process using Mailgun third-party email provider. First, we should Enable Members option on Ghost admin section then follow this guide to create a sending domain.

Once the working Mailgun domain is created, let’s define it in Ghost since it’ll be used to send emails when performing the password recovery process or simply to emails our subscribers on new articles published, the official guide explains it all.

This is the JAMStack methodology applied to enjoy faster page rendering and performance benefits using static page generation, more information is given into the official documentation where you'll see how important it’s to use the Content API Key to sync the frontend and the backend in a fashion way using the Content Library. The purpose is simply to enable the static generator behind to pull the data from your backend by using Ghost API.

It’s done by creating another Custom Integration on Ghost, then copying the generated Content API Key to be filled in on Vercel side using CMS_GHOST_API_KEY as environment variable key for the appropriate branch. We have covered both illustrations in previous steps already.

Once done, from the Deployments page, you can Redeploy (rebuild) your App and you are DONE.

Advantages of this Architecture

The decisions put in place for this architecture lead to many advantages that can even be improved with other techniques, here are some:

  • Allowing maximum scalability at multiple points: static assets (HTML, CSS, and images) being served from a CDN (Vercel and CloudFront), the content is close to our viewers/users locations.
  • Since the static generation happens a few times only, the backend will never be overloaded (small workload).
  • The RDS database can be scaled independently since it's an AWS-managed service, either by using multiple Read Replicas in case there are many read operations or by adopting a Multi-AZ strategy.
  • CI and CD are provided out of the box, even if we aren't deeply into DevOps the overall system is giving us that assurance that will save us some time to update our development and production environments on the fly.
  • Once done, we are fully covered from the technical aspect so we can immediately focus on content production, this article explains this choice.

An improvement could be to deploy Ghost on AWS side by using a compute service into a VPC where will also reside the RDS database, to improve the security aspect of the backend since the database will be accessible from within our virtually isolated network only.

Conclusion

In this article, we covered all the steps we put in place to set up our blog and we hope this can serve as an inspiration or a guide to build something better.