Cloud Storage instead of Vercel

Build, deploy and host static Next.js apps with all the bells and whistles.

Matías Battocchia
5 min readNov 11, 2023

Vercel and Netlify are first choices for deploying Netx.js apps, both offer a free plan for starters, git integrations and amazingly fast build times.

Aforementioned services’ free plan do not come with collaboration features, they are just for personal projects. For a side-project that you are developing with someone else, there are the advanced plans which do include teams, nonetheless, at the present state of our project they seem a bit pricey for us.

In our case, as we are using Supabase as database and backend, our frontend is a static Next.ja app. The fact that the front does not require a runtime opens the door to other classic and simpler hosting options. Leveraging possibilities, S3 and Cloud Storage are obvious candidates, then if your are using GitHub too, there is GitHub Pages (how-to).

Finally, we decided to go with Google Cloud because of its free tier, which also allows custom domains.

https://stablediffusionweb.com/#ai-image-generator

In this guide we will see:

  1. How to host a static app on a Cloud Storage bucket.
  2. How to setup GitHub Actions for continuous delivery.

Prerequisites

  • A Google Cloud project 🖥️
  • A GitHub repo 🗃️
  • A domain 🌐

Next.js configuration

The key configuration is to setup the output option to export.

// next.config.js

/**
* @type {import('next').NextConfig}
*/
const nextConfig = {
output: 'export',

// ...
}

module.exports = nextConfig

After running next build, Next.js will produce an out folder which contains the HTML/CSS/JS assets for your application.

Additional tweaking and optimizations here:

Cloud Storage

I followed this detailed step-by-step guide, there is no worth in reproducing it here. This section contains a summary of my path and some notes of my own.

Step 1: Create some buckets

  1. I created two buckets, one for staging and another one for production.
  2. I uploaded out folder contents to both buckets. Although in the end GitHub Actions will be used, to have at least an index.html is crucial for SSL certificate provisioning (down below).
  3. Set bucket permissions:
    a) Remove public access prevention.
    b) Give allUsers principal the Storage Object Viewer role.

At this point this public URL should work, even though the app might not because of the base URL: https://storage.googleapis.com/agendar-staging/index.html.

Step 2: Set up a HTTPS load balancer

  1. Frontend. I created a static IP and a Google-managed certificate for the staging and production subdomains.
  2. Backend. I created two services, one for each bucket: staging and production. I did not enabled Cloud CDN (no free tier).
  3. Routing rules.

Step 3: Connect your domain to the load balancer

I used the Cloud DNS service in order to redirect traffic to the static IP. I had to instruct the domain’s registrar to use Google Cloud name servers.

The provisioning of the SSL certificate takes some time (one hour or so). Be patient!

Workload identity federation

You can configure your deployment pipelines to authenticate to Google Cloud by using workload identity federation. This approach eliminates the maintenance and security burden associated with service account keys.

The GitHub Action will mint a GitHub OIDC token and exchange the GitHub token for a Google Cloud access token. This all happens without exporting a Google Cloud service account key JSON! Plus you do not need to make any configuration changes in your GitHub account.

As in the previous section, there is a complete guide for this one too. Once more, here are a summary and some notes.

Step 1: Create the workload identity pool and provider

  1. Attribute mappings. I created the mapping that the guide recommends or uses as an example. Mapping are useful later (step 3), the truth is that I did not use it after all.
  2. Attribute conditions. If I had not had at least one condition, anyone reading this post could potentially create a repo and configure a workflow that authenticates with the Google Cloud project I used.

When creating pool and provider, save this piece of information for later: https://iam.googleapis.com/projects/431304689009/locations/global/workloadIdentityPools/agendar/providers/github.

Step 2: Create a service account for the deployment pipeline

I created a specific service account for the workflow, I gave it the Storage Object User role since it will need it for syncing files on the buckets.

Keep the service account email for later:
github-actions@agendar-391402.iam.gserviceaccount.com.

Step 3: Allow the deployment pipeline to impersonate the service account

I used the Grant access UI button to give workload identities access to the previously created service account.

At the Only identities matching the filter option, I left it unselected. This is the place where attribute mappings can be used. For example, the subject attribute uses information from the GitHub Action job context; you could make that access token requests may only be granted for workflows running in specific branches. Here is more:

GitHub Actions

I setup two workflows that trigger on push: staging, which targets the develop branch, and production which does the same for the main branch. Here is the one I used for staging:

# .github/workflows/staging.yaml

name: Staging release

on:
push:
branches:
- develop
workflow_dispatch:

jobs:
ui-release:
runs-on: ubuntu-latest

env:
NODE_ENV: production # Next.js allows development, test, production - no staging!

permissions: # required by google-github-actions/auth
contents: read
id-token: write

steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v3
with:
node-version: '20.x'
cache: 'yarn'
cache-dependency-path: 'ui/yarn.lock'
- run: yarn --frozen-lockfile --production=false # we need dev dependencies for building
- run: mv .env.staging .env.production # a hack for having staging in NextJS
- run: yarn run build

- uses: 'google-github-actions/auth@v1'
with:
workload_identity_provider: 'projects/431304689009/locations/global/workloadIdentityPools/agendar/providers/github'
service_account: 'github-actions@agendar-391402.iam.gserviceaccount.com'

- uses: 'google-github-actions/setup-gcloud@v1'
- run: gsutil -m rsync -r -d out gs://agendar-staging

Note that the workload identity provider URL and the service account email are needed for authentication, none of them have to be kept secret.

Finally, gsutil rsync is used to efficiently upload the static assets to Cloud Storage.

--

--

Matías Battocchia

I studied at Universidad de Buenos Aires. I live in Mendoza, Argentina. Interests: data, NLP, blockchain.