Deploying NextJs v9 on Google App Engine

NextJS v9 offers new exciting features like “File system-based dynamic routing” and “Automatic Static Optimization”, but deploying on Google App Engine introduced some new challenges. 

In this post I will walk you through the steps. If you would like to skip ahead you can check out the working repository here

So first why use NextJS and why use Google App Engine? 

NextJs is a react framework that allows for static generated or server-side generated sites. So instead of the browser pulling down all the javascript and rendering it, the javascript is rendered before deploying or on the server before serving to the browser. This has some advantages when dealing with search engine optimization and how crawlers discover new pages on your site. It also boosts the sites loading time. The biggest advantage in my opinion is the ability to initially server a server rendered site and then once the javascript is loaded it turns into a single page application so the the user doesn’t have to wait for each browser refresh to go to a new page. 

Google App Engine is a managed server that automatically increases the server based on your traffic and also reduces the servers if there is no traffic. This allows new sites to keep costs to a minimum, up and coming sites to scale gracefully, and developers to focus on developing instead of devops. 

Quick Start


  1. Google Cloud SDK  – This is an SDK installed locally on your computer that allows you to deploy applications  and manage various things on your Google Cloud
  2. Clone the NextJs 9 GAE Demo    – You can start fresh with a Quickstart from NextJS but all of the below explanations have already been integrated. 

Configuring a New GCP Project and Application

If you have not set up a project and application to run this demo, follow the steps below. Otherwise, skip to the next section.

First, authenticate to GCP if you have not already:

gcloud auth login

Using the GCP CLI, create a new project and application. Replace PROJECT-NAME with your own.

gcloud projects create PROJECT-NAME
gcloud config set project PROJECT-NAME
gcloud app create

Wait a few minutes while Google provisions resources. While you are waiting, enable the Cloud Build API for your project by visiting the Cloud Build API page for your project (ex:

Building and Deploying the Application

npm run build
npm run deploy


Visit the link provided by GCP to test.



module.exports = {
  distDir: 'build',
  • Google App Engine doesn’t recognize .next folder so you need to change the folder from .next to build using the distDir parameter in the next.config.js


env: standard
runtime: nodejs12
service: default

  - url: /.*
    secure: always
    script: auto
  • The newest version of nextjs has dependencies that require nodejs12, so setting the runtime to nodejs12 fixes that issue
  • the /.* ensures access to all folders and sub folders within the build folder
  • secure always ensures https is used
  • script auto tells GAE to rely on nextjs to indicate what files to load instead of index.js

package.json -> scripts

start": "next start -p 8080
  • GAE runs on port 8080 and uses the start script by default in the package.json file

package.json -> scripts

"build": "rm -rf ./build && NODE_ENV=production next build",
"start": "next start -p 8080",
"deploy": "npm run build && gcloud app deploy"
  • the build script command removes the existing build folder and then runs a new build, this prevents nextjs making multiple versions and running into issues uploading many unnecessary previous builds during deploy
  • the deploy script command runs the build script and the app deploy command
  • if your fearless “deploy”: “npm run build && gcloud app deploy –quiet” use the quiet tag so you don’t have to type “y” to confirm deploy


You should only be upload the build folder and public folder to GAE. To prevent uploading source code you should add these lines to the .gcloudignore files. /pages is just the standard folder for basic next projects, if you have more like /components/, or /store/ you should add those too.

# Next.js source code

Dynamic Catch All Routes

If you are using the dynamic catch all route blog/[…slug].js then you might get this error

ERROR: ( INVALID_ARGUMENT: Filename cannot contain '.', '..', '\r', start with '-', '_ah/', or '\n': blog/[...slug]/index.js

Basically GAE doesn’t like when files names have … in them. To fix this your package.json scripts should now be

"gcp-predeploy": "find ./build -name '\\[...*' -exec bash -c 'mv "$1" "${1/.../@@@}"' -- {} \\;",
"gcp-build": "find ./build -name '\\[@@@*' -exec bash -c 'mv "$1" "${1/@@@/...}"' -- {} \\;",
"build": "rm -rf ./build && NODE_ENV=production next build",
"start": "next start -p 8080",
"deploy": "npm run build && npm rub gcp-predeploy && gcloud app deploy"

So now the deploy script runs build which removes the existing build folder, builds a new one, then replaces the dynamic all route file names from […slug].js to [@@@slug].js, deploys the app, and once the app is uploaded GAE runs gcp-build and renames them back to […slug].js

This fix was taken from here and modified

About the Author

Brad Risse profile.

Brad Risse

Sr. Consultant
Leave a Reply

Your email address will not be published. Required fields are marked *

Related Blog Posts
Using Conftest to Validate Configuration Files
Conftest is a utility within the Open Policy Agent ecosystem that helps simplify writing validation tests against configuration files. In a previous blog post, I wrote about using the Open Policy Agent utility directly to […]
SwiftGen with Image & Color Asset Catalogs
You might remember back in 2015 when iOS 9 was introduced, and we were finally given a way to manage all of our assets in one place with Asset Catalogs. A few years later, support […]
Tracking Original URL Through Authentication
If you read my other post about refreshing AWS tokens, then you probably have a use case for keeping track of the original requested resource while the user goes through authentication so you can route […]
Using Spring Beans in a Kafka Streams ExceptionHandler
There are many things to know before diving into Kafka Streams. If you haven’t already, check out these 5 things as a starting point. Bullet 2 mentions designing for exceptions. Ironically, this seems to be […]