Deploy static websites using github pages, vue, angular, Nx and cloudflare

How to Deploy Static Websites with Nx, GitHub, and Cloudflare?

A question was raised in a forum asking how to deploy static websites for free. Then, more specifically, it was mentioned they were using Nx to generate multiple websites. In this article, we will create a new Nx project with a few apps, publish our code to github, deploy all the websites to GitHub actions, and set up Cloudflare to share our custom domain. Prerequisites: git and nodejs installed.

Setup a New Nx Project

That’s easy. We can use npx create-nx-workspace. We answer some setup questions. Here are my answers:

Figure 1: Nx Workspace setup questions

For this case, the most important answer is that we’d like an integrated monorepo. This allows us to create multiple projects and deploy them easily.

Once this is setup, we cd into our project (in my case, it is cd y-static-websites). Inside, we can install our wanted packages. Let’s create three projects: vue, angular and web.

Install the Web, Vue and Angular Packages

npm i -D @nx/angular @nx/vue @nx/web

Create the projects

npx nx g @nx/web:project apps/app1
npx nx g @nx/vue:project apps/app2
npx nx g @nx/angular:project apps/app3

The above commands will generate 3 projects for us inside the apps folder. For the first two I opted for vite as the builder. For the angular project, I’ve selected esbuild as the builder.

Each app can be developed using npx nx run {appName}:serve (e.g. nx run app1:serve which loads a dev server for us. But right now, we are focused on deploying our websites.

Configure the Builders to Serve the Applications

Because we are going to serve all websites from the same top domain, we need to tell the browser where to look for the files. In app1 and app2, we can just add the base property to vite.config.ts:

export default defineConfig({
  root: __dirname,
  cacheDir: '../../node_modules/.vite/apps/app1',

  server: {
    port: 4200,
    host: 'localhost',
  },

  preview: {
    port: 4300,
    host: 'localhost',
  },

  plugins: [nxViteTsPaths()],

  // Uncomment this if you are using workers.
  // worker: {
  //  plugins: [ nxViteTsPaths() ],
  // },

  base: '/app1/',
  build: {
    outDir: '../../dist/apps/app1',
    emptyOutDir: true,
    reportCompressedSize: true,
    commonjsOptions: {
      transformMixedEsModules: true,
    },
  },
});

Note the base: '/app1/' property added to the config. We do the same for app2 (only with base: '/app2/').

For the angular app, we add baseHref to the build configuration in project.json:

"targets": {
    "build": {
      "baseHref": "/app3/browser/",
... rest of config goes here

Notice that the angular build is set inside the app3/browser folder.

Now we are ready to test if this works.

Build the Apps

We build the apps in a single command:

npx nx run-many --target=build --all

and dist is generated for us.

Test the Apps Locally

In order to test that the apps load locally, let’s cd into the main deploy folder: cd dist/apps.

Once there, we can setup a local static server like this: npx static-server. This loads a simple server that works kind of like github pages.

It will output the URL where the server is served (default is http://localhost:9080). Browse the URL and append the application’s folder. So app1 will be: http://localhost:9080/app1, and app3 will be http://localhost:9080/app3/browser.

If everything works fine, all 3 websites should work.

Load the Code to Github

Loading the code to github requires to create a new github repository.

Create a new github repository (or browser here). Once done, follow the instructions to push an existing repository from the command line:

git remote add origin https://github.com/YonatanKra/my-static-websites.git
git branch -M main
git push -u origin main

Don’t forget to replace the origin URL with yours. And, of course, you should run it from the project’s root (just in case you are still in dist/apps 😉 ).

How to Deploy Apps to Github Pages Using Github Actions?

Now our repository is on Github! Hooray. We want to activate Github pages with Github actions.

We can set it up in the repository’s settings (orange arrow in Figure 2).

Figure 2: The steps to create github pages. The order of arrows is: Orange, Yellow, Purple, White

Once inside Settings, go to Pages (Yellow arrow). Open the select box (Purple arrow) and make sure to select Github Actions. This will display the option to configure Static HTML deployment (White arrow).

After hitting Configure a new screen will open with a workflow file ready to commit. It looks like this:

# Simple workflow for deploying static content to GitHub Pages
name: Deploy static content to Pages

on:
  # Runs on pushes targeting the default branch
  push:
    branches: ["main"]

  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:

# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
  contents: read
  pages: write
  id-token: write

# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
  group: "pages"
  cancel-in-progress: false

jobs:
  # Single deploy job since we're just deploying
  deploy:
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Setup Pages
        uses: actions/configure-pages@v5
      - name: Upload artifact
        uses: actions/upload-pages-artifact@v3
        with:
          # Upload entire repository
          path: '.'
      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v4

The important part is the on section and the steps phase.

on tells github actions when to run. In our case, on every push to the main branch.

steps is what is actually being done in the deploy job.

It currently checks out our code from the repository (the Checkout step), setup github pages, uploads some artifacts (currently the whole project), and finally deploys to GitHub pages.

What we’d like to do is the following:

  1. Checkout
  2. Npm install
  3. Build
  4. Configure pages
  5. Upload dist/apps as artifacts
  6. Deploy to github pages

So our code will look like this:

# Simple workflow for deploying static content to GitHub Pages
name: Deploy static content to Pages

on:
  # Runs on pushes targeting the default branch
  push:
    branches: ["main"]

  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:

# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
  contents: read
  pages: write
  id-token: write

# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
  group: "pages"
  cancel-in-progress: false

jobs:
  # Single deploy job since we're just deploying
  deploy:
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Npm install
        run: npm ci
      - name: Build
        run: npx nx run-many --target=build --all
      - name: Setup Pages
        uses: actions/configure-pages@v5
      - name: Upload artifact
        uses: actions/upload-pages-artifact@v3
        with:
          # Upload apps dist folder
          path: './dist/apps'
      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v4

Notice the steps added Npm install, Build and the change to upload artifact to upload only the dist/apps folder.
Here’s the repository: https://github.com/YonatanKra/my-static-websites

Our websites won’t show now. We need to set up our custom domain in Cloudflare.

Setup Our Domain in Github

Let’s assume we have the domain yonatankra.com. We’d like to set up a subdomain my-static-websites.yonatankra.com to redirect to our GitHub pages static websites.

First, let’s set it up in the Github repository’s settings. Go back to Settings => Pages and find the Custom domain input box. Write down your custom domain (in my case my-static-websites.yonatankra.com, but in your case, it should probably be different).

After you save the domain, it will try to verify its settings and will probably fail. That’s because we need to change the domain’s settings in Cloudflare.

Setup Our Domain in Cloudflare

This step assumes you already have a Cloudflare account. If you do not, create one.

There are several steps to set up your domain in Cloudflare:

  1. Create a new website (purple arrow in Figure 3)
  2. Either transfer your domain or register a new one in Cloudflare
  3. Choose the Free program
  4. Follow the instructions to set up your domain on Cloudflare
Figure 3: Adding a new Site to your CloudFlare account

Once the domain is set up, you should be able to add DNS records (see DMS Records screen in Figure 4).

Figure 4: DNS Records page

On this page, add 3 new A records, as shown in Figure 5. Note that you need to add the 3 records because Github pages work with 3 IP addresses:

185.199.109.153
185.199.110.153
185.199.111.153
Figure 5: Add a new record. Do this 3 times for the 3 IP addresses.

Once that’s done, your domain will direct to your GitHub pages. If you go to the URL my-static-websites.myDomain.com, you will get an infinite redirect error. That’s because we need to define how SSL is handled in Cloudflare. By default, the SSL is set to Flexible. Github pages do not support it, so you need to define it to Full for this subdomain.

Under the SSL tab (figure 6) you will have a link to Configuration Rule creation. Click it.

Figure 6: The SSL Tab

You will be redirected to the Configuration Rules page (figure 7).

Figure 7: The Configuration Rules Page

Click on Create rule. Set a rule name and set the condition as shown in Figure 8 (remember to change the domain name). This will ensure that this rule applies only to this subdomain.

Figure 8: A new Configuration Rule

If you create more Github actions in the future, you can add more incoming request matches. Now, we need to define the rule. Scroll down until you see the SSL box (Figure 9).

Figure 9: The SSL box sets the configuration to FULL encryption mode

After you have made sure the encryption mode is full, click on Deploy.

Now you can browse the 3 static websites and do whatever you want with them:

https://my-static-websites.yonatankra.com/app1

https://my-static-websites.yonatankra.com/app2

https://my-static-websites.yonatankra.com/app3/browser

Summary

This guide might seem a bit long but it shouldn’t take more than 5 minutes after the first initial setup.

Of course, you can optimize the GitHub actions flow, but that’s a topic from a different article.

Sign up to my newsletter to enjoy more content:

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments