Deploying a serverless NextJS App
For a while, I have been wanting to try server-side rendering, specifically with NextJS. Recently, a big pull request landed in refinebio’s repository with the entire migration.
We had a few discussions to decide how to deploy these changes. We liked Labda@Edge and considered the idea of deploying it with serverless since they had a component specifically for NextJS.
We also came across ZEIT, they seemed to offer the ability to deploy a NextJS site to CDNs, just like Lambda@Edge
, with no configuration. I usually get worried when I hear that, because hitting a corner case that’s not covered it’s bound to happen. Still, we decided to give it a try.
Considerations with ZEIT
The initial integration was really easy, we used their github integration and deployed to a *.now.sh
free domain. However, two issues arose when we tried to set up two domains for staging and production.
The first one was a bug on ZEIT’s platform. In our repo we have two main branches, master
and dev
which we use for production and staging respectively. The default one is dev
. It seemed that ZEIT had some problems when identifying the default branch in the repository and they were not letting me deploy the master
branch.
I filed a support ticket and talked with the engineers to resolve this. The experience was really good, and they addressed it in less than 48 hours. This was critical because I was starting to consider the idea of using a different platform to deploy our project.
The second problem we faced was setting up our staging server and have it use our staging API. In the frontend, this is controlled with an environment variable where we set the URL of the API that should be used.
At the time of writing ZEIT, still didn’t support having different environment variables for each deployment, although they say they are actively working on it. To go around this, I had to disable the automatic deployments with their Github’s integration and trigger the deploys manually with continuous integration (in our case CircleCI).
I saw a similar config and ended up with:
version: 2
jobs:
deploy-prod:
docker:
- image: circleci/python:3-node
steps:
- checkout
- run: yarn global add now
- run:
command: $(yarn global bin)/now --confirm --prod --token $ZEIT_TOKEN --local-config now.json --scope ccdl
deploy-staging:
docker:
- image: circleci/python:3-node
steps:
- checkout
- run: yarn global add now
- run:
command: $(yarn global bin)/now --confirm --prod --token $ZEIT_TOKEN --local-config now.staging.json --scope ccdl
workflows:
version: 2
test-and-deploy:
jobs:
- deploy-staging:
filters:
branches:
only: dev
- deploy-prod:
filters:
branches:
only: master
The main difference between the two is the config files, the one for staging now.staging.json
has the following
{
"version": 2,
"alias": ["staging.refine.bio"],
"name": "refinebio-frontend",
"build": {
"env": {
"REACT_APP_API_HOST": "https://api.staging.refine.bio"
}
},
"github": {
"autoAlias": false,
"silent": true
}
}
The options under github
serve two purposes:
-
autoAlias
is to disable automatic production domain updates so that only the domains inalias
get updated. -
silent
disables all comments on Github, by default there were comments in our pull requests every time they were deployed. We prefer to just use the links on the checks offered by Github.
Conclusion
I’d still recommend ZEIT, I had a very good experience with their technical support and even if their “zero-configuration” tools don’t work for your use case, they still offer other lower-level tools that can be customized further. Like the Now CLI command.
Pay special attention if you have a complicated staging/production setup, because you might need to use the Now CLI with a custom CI configuration like the one I had to use.