Gitlab CI/CD with zeit.co's now

Update: This article is no longer relevant. The NOW API referred to below is depcrecated and I've since moved on to running Dokku on a VPS.

I recently had the opportunity to play around with zeit.co's deployment service (also where this blog is hosted). I used Gitlab's pipelines to set up a docker container deployment workflow that includes automated review and staging builds and a manual step for deploying master to production.

Update: I edited the gitlab pipeline to clean previous deployments and name deployments with prefixed $APPS_DOMAIN in order to have a less confusing list on zeit.co's deployment list. Also, all but production builds now clean previous deployments automatically on a new deployment. The $NOW_REGION variable is no longer necessary, instead all region specific settings are in now.json using "scale".

Update 2: now updated to use their serverless Docker infrastructure. Handle no existing deployments when trying to remove old deployments. Dockerfile: remove devDepencencies after build.


  • The review stage is active for all branches except master. So whatever your branch is called, it creates a now deployment with a subdomain based on the branch name.
  • Each commit to master triggers a refresh of the staging subdomain.
  • Each staging build can then be manually deployed to replace the current production container.

Gitlab and Now

  • It requires two Gitlab CI/CD variables: a now token and the domain of your app.
  • now region: Gitlab runners are located somewhere in North America and since now uses the (I guess) GeoIP information of the machine that launches a now build, it always goes for a North American datacenter, in my case "sfo". But I want my build and subsequent deployment to end up in europe, so I go for "bru" (Brussels) there.
  • now teams: Since I use a zeit.co account that is linked to a team and the deployment is using the team accounts domain, I need to specify the team name as a parameter for the now binary. If you don't need that, you can remove the variable's content.
  • now domains: For convenience, I also host my domain / zone file on zeit. That allows for some very neat and easy aliasing.

For this to work properly, you need to be able to run now from your local machine (e.g. now.json config or a now-key in package.json and a Dockerfile). If your deployment runs fine locally, it will - quite probably - do so in a Gitlab runner instance.

A note about regions

While testing zeit.co without applying any scaling I noticed that the TTFB is significantly higher than with my previous hosting environment. Somewhere in a constant 800ms range - from brussels. And I guess this is why: containers on now that do not have scaling configured will be frozen after a certain timeout. With scaling configured (e.g. in now.json), TTFB is almost halved. I guess that non-scaling deployments are behind a different load balancer setup than scaled deployments. Or something like that…

I switched off zeit.co's domain CDN to save a few bucks (nobody cares if this blog loads in 400ms or 1.5s…), turned on scaling and voila, the datacenter in Brussels seems to be doing just fine for European traffic. I ran a few tests on pingdom.com via San Jose and the TTFB from the eastern US is about 2.5s. I'm not concerned with that currently but might extend my config to spin up 2 instances in SFO and BRU, just to see what would be possible.


now.json: name the project the same as $APP_DOMAIN-production in order for it to pick up aliases. I set the region to "bru1" for europe and set "scale" to minimum 1 instance and maximum 3. Set this according to your requirements.

Content Plugin missing (blockSourceCode)
Content Plugin missing (blockSourceCode)

It takes a while to build during rush hours (well, I'm using Gitlab's free runners, so I don't expect any miracles). But all in all, this is a very simple, very effective way to run some minor devops for a nextjs app.

For completeness, here's my Dockerfile:

Content Plugin missing (blockSourceCode)

Photo by Scott Webb on Unsplash

Published: 08/22/2018

© 2020 genox.ch – emaillegal