How to deploy a Next.js application to a custom server (DigitalOcean, Linode, …) using Bitbucket Pipelines
Assuming, that you:
- are developing a Next.js application
- host your Git repository on Bitbucket
- want to run your Next.js app on a virtual private server, like DigitalOcean, Linode or any other machine you can ssh into
- want your commits to be automatically published to your server
- appreciate different environments for testing/staging/production based on git branches
Setting up Bitbucket Pipelines
Most of the steps required to setup Bitbucket Pipelines are documented well enough, so I’m just going to list the steps together with the relevant links here:
Getting Started: If you have never used Bitbucket Pipelines, I recommend reading the official guide for getting started with Bitbucket Pipelines first.
Exchange SSH keys: In order to enable your pipeline to talk to your server via ssh, you will need to set up SSH keys. Please follow along this article to do so.
Create the Pipelines file
Bitbucket Pipelines allow you to run commands in a fresh docker image that already contains your code every time you push a commit to a certain branch. We will use this to do a few things:
- install dependencies for the application (npm i)
Create a file called
I use the following configuration:
# Using the node image provided by bitbucket image: node:10.15.3 pipelines: branches: # run this on every commit to the branch "staging" staging: - step: name: buildAndDeploy # define which caches to use caches: - node # provided by bitbucket to cache node_modules - nextcache # see definitions section below script: # install rsync - apt-get update && apt-get -qq install rsync # install node modules - npm install # build Next.js app - npx next build # create deploy directory (to contain .next folder, package.json, node_modules, public) - mkdir deploy - cp -a .next ./deploy - cp package.json ./deploy - cp -a node_modules ./deploy - cp -a public ./deploy # rsync to a temp directory on remote server - rsync -arz --delete $BITBUCKET_CLONE_DIR/deploy/ $USER@$REMOTE_IP:/var/www/staging-temp # clear current serving directory, sync from temp directory to serving directory, restart next server - ssh $USER@$REMOTE_IP "rsync -ar --delete /var/www/staging-temp/ /var/www/staging && pm2 restart next-staging && rm -r /var/www/staging-temp" definitions: caches: nextcache: .next/cache
A few things to note here:
- By defining branches > staging we can specify that this script should only run on commits to branch
- You likely want to do something similar for other branches like
master, but deploy to a different server or folder depending on your setup.
- By creating a custom cache definition called
nextcachewe can cache the directory
.next/cachebetween builds as recommended by Next.js.
- In order to deploy only specific directories (.next, node_modules, …) I copy them to a
deploydirectory first (another approach would be to use a include-list for rsync)
- Target folder on the server is hardcoded here (
- to minimize the time it takes for the files to land in the
stagingdirectory I am transferring them to a
staging-tempfolder first, then rsync’ing them locally to
- The pm2 part of the script assumes you already have your Next.js app running using pm2
- This is a rather minimal setup, a more real world
bitbucket-pipelines.yamlwould probably include tests etc.
Add environment variables
If you don’t want certain pieces of information about your target server in your repository (like the IP address or the user), I recommend using environment variables for the build process.
In the Bitbucket Repository page, go to Settings → Pipelines → Repository Variables. In my example I used these two variables:
- Name: REMOTE_IP, Value: The IP address of your target server
- Name: USER, Value: the username to use for ssh
These Repository Variables can be used in the bitbucket-pipelines.yaml file using $-notation, like
$REMOTE_IP. There are also a bunch of default variables provided by Bitbucket.
Try it out!
If everything is setup correctly (especially ssh keys), your Next.js-app should be deployed to the target server after every commit to
staging branch. Visit the Pipelines section of your repository on Bitbucket (<your-repo-url/addon/pipelines/home) to watch what’s happening (or see what’s going wrong ⚠️).