Geek Culture
Published in

Geek Culture

Deploy a NextJs app to DigitalOcean using Github Actions and Docker

Nextjs, Github Actions, docker and DigitalOcean

In this tutorial, you will learn how to set up a continuous delivery of a Nextjs app using tools like Docker and Github Actions. We will use an Ubuntu (20.04) droplet on DigitalOcean to host our app.

Prerequisites

Here are the prerequisites required for this tutorial.

Create your app

To create a Next.js app, open your terminal, cd into the directory you’d like to create the app in, and run the following command:

npx create-next-app nextjs-blog

You now have a new directory called nextjs-blog. Let’s cd into it:

cd nextjs-blog

Then, run the following command:

yarn dev

This starts your Next.js app’s “development server” (more on this later) on port 3000.

Let’s check to see if it’s working. Open http://localhost:3000 from your browser.

http://localhost:3000

Dockerize your app.

Add a Dockerfile to the project root:

FROM node:13.1-alpineWORKDIR /usr/src/app
COPY package*.json ./
RUN yarn cache clean && yarn --update-checksums
COPY . ./
EXPOSE 3000
RUN yarn build && yarn start

yarn cache clean running this command will clear the global cache.
yarn --update-checksums lock lockfile if there's a mismatch between them and their package's checksum.

Let’s build and tag our docker image

docker build -t nextjs-blog .

Run the container once the build is done

docker run -it -p 3000:3000 nextjs-blog

Boom 💥! Our app is running on http://localhost:3000

Publish Your Image to Github Packages.

Github Packages gives you the option to publish and consume packages within your business or worldwide. To realize this, we will create a Github Action which will publish our package to the Github Packages Registry. Before we deploy our production image to the registry, we need to make sure that our code is production-ready.

deploy.yml

Let’s create our first deployment action in our project.

mkdir .github && cd .github && mkdir workflows && cd workflows && touch deploy.yml

The command above creates a workflow folder and a deploy.yml file. You can replace yarn with npm in the code below.

name: buildon:
push:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: yarn install
run: |
yarn
test:
runs-on: ubuntu-latest
needs: build
strategy:
matrix:
os: [ubuntu-latest]
node-version: [14.x]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: yarn install & test
run: |
yarn
yarn test
env:
CI: true
build-and-deploy:
runs-on: ubuntu-latest
needs: test
steps:
- uses: actions/checkout@v2
- name: Build image
run: docker build . --file Dockerfile --tag nextjs-blog --label "runnumber=${GITHUB_RUN_ID}"
- name: Log into registry
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
- name: Push image
run: |
IMAGE_ID=ghcr.io/${{ github.repository_owner }}/nextjs-blog
# Change all uppercase to lowercase
IMAGE_ID=$(echo $IMAGE_ID | tr '[A-Z]' '[a-z]')
# Strip git ref prefix from version
VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,')
# Strip "v" prefix from tag name
[[ "${{ github.ref }}" == "refs/tags/"* ]] && VERSION=$(echo $VERSION | sed -e 's/^v//')
# Use Docker `latest` tag convention
[ "$VERSION" == "main" ] && VERSION=latest
echo IMAGE_ID=$IMAGE_ID
echo VERSION=$VERSION
docker tag nextjs-blog $IMAGE_ID:$VERSION
docker push $IMAGE_ID:$VERSION

Note that Github Actions automatically provides you with GITHUB_TOKEN secrets.

Push to main

Created our remote repository, and set remote origins to our local repository. We are now ready to go ahead and push our changes to our remote repository.

git add .
git commit -am "Initial commit with deploy file"
git push origin main

If you click on actions, you will notice the start of the deployment workflow. Wait and see your image being published on your Github Packages Registry.

After actions are done, you can find your published docker image in your repository on the package tab.

Boom 💥 we successfully published our docker app image on the Github Package Registry. We are going to order a Docker Droplet on DigitalOcean and set up a flow to deploy and our app image on DigitalOcean.

Deploy.

For deployment, we are going to create a Docker Droplet on DigitalOcean. Please do not forget to sign up with my Referral Link and get $100 in credit for over 60 days.

Docker DigitalOcean

For this example, we access our droplet with a username and a password, please choose a one-time password over an SSH key.

After configuring and resetting your droplet password let’s now add your droplet secrets to your repository.

  • HOST: Droplet IP_ADDRESS
  • USERNAME: Droplet USERNAME
  • PASSWORD: Droplet PASSWORD

Update deploy.yml file.

You have succeeded in setting up your droplet secrets to your repository. You will now add another code block to deploy your package and run it in our droplet using ssh-action. It’s GitHub Actions for executing remote ssh commands.

Replace the deploy.yml content with the code below:

name: buildon:
push:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: yarn install
run: |
yarn
test:
runs-on: ubuntu-latest
needs: build
strategy:
matrix:
os: [ubuntu-latest]
node-version: [14.x]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: yarn install & test
run: |
yarn
yarn test
env:
CI: true
build-and-deploy:
runs-on: ubuntu-latest
needs: test
steps:
- uses: actions/checkout@v2
- name: Build image
run: docker build . --file Dockerfile --tag nextjs-blog --label "runnumber=${GITHUB_RUN_ID}"
- name: Log into registry
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
- name: Push image
run: |
IMAGE_ID=ghcr.io/${{ github.repository_owner }}/nextjs-blog
# Change all uppercase to lowercase
IMAGE_ID=$(echo $IMAGE_ID | tr '[A-Z]' '[a-z]')
# Strip git ref prefix from version
VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,')
# Strip "v" prefix from tag name
[[ "${{ github.ref }}" == "refs/tags/"* ]] && VERSION=$(echo $VERSION | sed -e 's/^v//')
# Use Docker `latest` tag convention
[ "$VERSION" == "main" ] && VERSION=latest
echo IMAGE_ID=$IMAGE_ID
echo VERSION=$VERSION
docker tag nextjs-blog $IMAGE_ID:$VERSION
docker push $IMAGE_ID:$VERSION
- name: Deploy Container to Digitalocean
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
password: ${{ secrets.PASSWORD }}

script: |
echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
IMAGE_ID=ghcr.io/${{ github.repository_owner }}/nextjs-blog
# Change all uppercase to lowercase
IMAGE_ID=$(echo $IMAGE_ID | tr '[A-Z]' '[a-z]')
# Strip git ref prefix from version
VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,')
# Strip "v" prefix from tag name
[[ "${{ github.ref }}" == "refs/tags/"* ]] && VERSION=$(echo $VERSION | sed -e 's/^v//')
# Use Docker `latest` tag convention
[ "$VERSION" == "main" ] && VERSION=latest
echo IMAGE_ID=$IMAGE_ID
echo VERSION=$VERSION
docker pull $IMAGE_ID:$VERSION
docker update --restart=no nextjs-blog || true
docker stop $(docker ps -a -q) || true
docker rm $(docker ps -a -q) || true
docker run -d --restart unless-stopped -p 3000:3000 --name nextjs-blog $IMAGE_ID:$VERSION

nextjs-blog is the name that i choosed for the image name, you can replace it by anything you want

Let’s commit and push our changes to master.

git add .
git commit -am "edit: update deploy.yml to deploy app into digitalocean"
git push origin main

We’re using the ssh-action to remotely access our droplet from our repository.

Congratulations 🎉! You can now access your Nextjs app on your droplet IP_ADDRESS or DOMAIN_NAME with 3000 port.

https://themeptation.medium.com/deploy-a-nextjs-application-using-gitlab-ci-cd-docker-on-digitalocean-droplet-ubuntu-18-04-d8163c64a893

Conclusion

In this tutorial, you successfully created a NextJS application, wrote Docker and Github Actions configurations to deploy the application on DigitalOcean docker droplet. With this, you are set to configure other projects in the future.

Chime in and leave your thoughts and suggestions in the comments below.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Said BADAOUI

Said BADAOUI

JavaScript || Reactjs || Nextjs || Nodejs || Python || Front-end Developer