skip to main content
Avatar of Nicklas Jarnesjö
Nicklas Jarnesjö

Deploying Next.js with Docker and Github actions to Digital Ocean

code

Here comes another post about Github actions and deployment to Digital Ocean. This is from this site which is deployed with exactly this manner.

Why don't just go with Vercel?

It's a valid question and I have done in this way because

  1. Want to learn and get a better understanding of Github Actions
  2. Want to learn Docker
  3. I/We have most of our sites on Digital Ocean

Dockerfile

With this config you build multi stage environment, but in my case I only do it with production.

Dockerfile
# Source: https://github.com/vercel/next.js/discussions/16995#discussioncomment-132339

# Install dependencies only when needed
FROM node:lts-alpine AS deps

WORKDIR /opt/app
COPY package.json package-lock.json ./
RUN npm ci

# Rebuild the source code only when needed
FROM node:lts-alpine AS builder

# Add Google Analytics to client code
ARG NEXT_PUBLIC_GOOGLE_ANALYTICS_ID
ENV NEXT_PUBLIC_GOOGLE_ANALYTICS_ID=$NEXT_PUBLIC_GOOGLE_ANALYTICS_ID

ENV NODE_ENV=production
WORKDIR /opt/app
COPY . .
COPY --from=deps /opt/app/node_modules ./node_modules
RUN npm run build

# Production image, copy all the files and run next
FROM node:lts-alpine AS runner

WORKDIR /opt/app
ENV NODE_ENV=production
COPY --from=builder /opt/app/next.config.js ./
COPY --from=builder /opt/app/public ./public
COPY --from=builder /opt/app/.next ./.next
COPY --from=builder /opt/app/node_modules ./node_modules

# Next.js collects completely anonymous telemetry data about general usage.
# Learn more here: https://nextjs.org/telemetry
# Uncomment the following line in case you want to disable telemetry.
# ENV NEXT_TELEMETRY_DISABLED 1

CMD ["node_modules/.bin/next", "start"]

Github action

Here we are using Github package registry to distribute the package and the login to Digital Ocean and deploy it. All the secrets in this file are described below.

.github/workflows/build-and-deploy.yml
name: Build and Deploy

# Controls when the action will run. Triggers the workflow on push or pull request
# events but only for the master branch
on:
  push:
    branches: [main]
  workflow_dispatch:
    inputs:
      logLevel:
        description: "Log level"
        required: true
        default: "warning"

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  # This workflow contains a single job called "build"
  build:
    # The type of runner that the job will run on
    runs-on: ubuntu-latest
    container: node:14

    # Steps represent a sequence of tasks that will be executed as part of the job
    steps:
      # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
      - uses: actions/checkout@v2

      - name: Build and Publish to Github Packages Registry
        uses: elgohr/Publish-Docker-Github-Action@master
        env:
          NEXT_PUBLIC_GOOGLE_ANALYTICS_ID: ${{ secrets.APP_NEXT_PUBLIC_GOOGLE_ANALYTICS_ID }}
        with:
          name: jarnesjo/jarnesjo.com/nextjs
          registry: docker.pkg.github.com
          username: ${{ secrets.USERNAME }}
          password: ${{ secrets.GITHUB_TOKEN }}
          dockerfile: Dockerfile
          buildargs: NEXT_PUBLIC_GOOGLE_ANALYTICS_ID
          tags: latest

      - name: Deploy package to digitalocean
        uses: appleboy/ssh-action@master
        env:
          GITHUB_USERNAME: ${{ secrets.USERNAME }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          host: ${{ secrets.DEPLOY_HOST }}
          username: ${{ secrets.DEPLOY_USER }}
          key: ${{ secrets.DEPLOY_KEY }}
          port: ${{ secrets.DEPLOY_PORT }}
          envs: GITHUB_USERNAME, GITHUB_TOKEN
          script: |
            docker login docker.pkg.github.com -u $GITHUB_USERNAME -p $GITHUB_TOKEN
            docker pull docker.pkg.github.com/jarnesjo/jarnesjo.com/nextjs:latest
            docker stop jarnesjo.com
            docker system prune -f
            docker run --name jarnesjo.com -dit -p 3000:3000 docker.pkg.github.com/jarnesjo/jarnesjo.com/nextjs:latest

Github secrets

APP_NEXT_PUBLIC_GOOGLE_ANALYTICS_ID - Google Analytics code injected to client code
DEPLOY_HOST - IP to Digital Ocean (DO) droplet
DEPLOY_KEY - SSH secret and the public key should be added to `.ssh/authorized_keys` on server
DEPLOY_PORT - SSH port (22)
DEPLOY_USER  - User on droplet (for Forge: `forge`)
USERNAME - Your Github username

Hope it can give you inspiration to your setup up and if you don't want to go with standard Vercel setup.

Discuss this post on Twitter