25th July 2024
6 min read
David Hazra
David Hazra is a professional software developer based in London
Caption: Docker, GitHub Actions, and AWS ECR
Deploying Next.js on platforms other than Vercel is a viable and cost-saving alternative. To do this in a secure and convenient way it is common to containerise your application so it can be run from anywhere in a common format.
In this blog post, we'll explore how to deploy a Next.js application on Kubernetes using a CI/CD pipeline. By the end, you'll have a solid understanding of how to containerise your Next.js application, set up continuous integration with GitHub Actions, and implement continuous delivery to AWS' Elastic Container Registry (ECR).
A Next.js Application: Your Next.js application should be set up and hosted in a GitHub repository. Ensure your codebase is clean and your application runs correctly.
An AWS Account and ECR Repository: An AWS account is necessary. Within your AWS account, create an ECR repository to store your Docker images. You can do this through the AWS Management Console under the ECR service.
GitHub Actions Workflow Permissions: Set up GitHub Actions in your repository. Ensure your repository has the necessary permissions to use GitHub Actions and to push Docker images to AWS ECR. Store your AWS credentials as secrets in your GitHub repository. Go to your repository settings, navigate to "Secrets and Variables" under the "Security" section, and add your AWS access key ID and secret access key.
The first step in the process is to containerise your Next.js application so that it can be served in a repeatable fashion. This is done using Docker with a Dockerfile.
First, you need to create a Dockerfile in the root directory of your Next.js project. This file contains the instructions Docker will use to build your application image. Here’s an example Dockerfile for a Next.js application:
Below is an example of a very simple image build script, more advanced concepts could take advantage of using builder images, or special build arguments to reduce memory usage.
# Official Node.js image
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm clean-install
COPY . .
RUN npm run build
CMD ["npm", "start"]
Why Use npm clean-install
: The npm clean-install
command installs dependencies based on the package-lock.json
file, ensuring a consistent and reproducible build. This command is faster and more reliable than a standard npm install
, as it skips certain steps like generating a package-lock.json
file and only installs exact versions specified in it. Using npm clean-install
helps avoid potential issues from cached modules or differences in dependency versions.
Why no EXPOSE
: The EXPOSE
instruction in a Dockerfile is not strictly necessary for Kubernetes deployments. In Kubernetes, service configurations and deployment manifests handle port mapping and expose container ports to the external world. Therefore, omitting the EXPOSE instruction in the Dockerfile is perfectly fine when the container will be managed by Kubernetes.
To test that your docker build will work, you can do it locally
docker build -t nextjs-app:latest .
If you'd also like to push the image to ECR yourself instead of using GitHub Actions (as explained in the next section) you can do the following:
Log in to ECR using the Docker CLI
aws ecr get-login-password --region <aws-region> | docker login --username AWS --password-stdin <aws-account-id>.dkr.ecr.<aws-region>.amazonaws.com
Then tag your docker image
docker tag nextjs-app:latest <aws-account-id>.dkr.ecr.<aws-region>.amazonaws.com/nextjs-app:latest
Finally, push to the ECR repo
docker push <aws-account-id>.dkr.ecr.<aws-region>.amazonaws.com/nextjs-app:latest
Pushing to ECR manually is less secure and less convinient than having an automated process do the build and push cycle for you.
There are many options for automating workflows (Jenkins for example), but perhaps the simplest to get running is Github Actions.
Create a GitHub Actions workflow file in your repository. This file defines the steps to build and push the Docker image to AWS ECR. Create a directory named .github/workflows
in the root of your repository and add a file named deploy.yml
with the following content:
name: NextJS CI
on:
push:
paths:
- "website/**"
- ".github/**"
jobs:
image-build-push:
runs-on: ubuntu-latest
env:
AWS_DEFAULT_REGION: eu-west-2
ECR_REPOSITORY: nextjs-app
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Use Node.js 20
uses: actions/setup-node@v4
with:
node-version: "20"
- name: Install dependencies
working-directory: website
run: npm clean-install
- name: Run lint
working-directory: website
run: npm run lint
- name: Get short SHA
run: |
SHORT_SHA=$(echo $GITHUB_SHA | cut -c 1-6)
IMAGE_TAG=${{ github.run_number }}_$SHORT_SHA
echo "IMAGE_TAG=$IMAGE_TAG" >> $GITHUB_ENV
- name: Checkout repository
uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ env.AWS_DEFAULT_REGION }}
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
- name: Build docker image
working-directory: website
run: docker build -t ${{ steps.login-ecr.outputs.registry }}/$ECR_REPOSITORY:$IMAGE_TAG .
- name: Push image to ECR
if: github.ref == 'refs/heads/master'
run: docker push ${{ steps.login-ecr.outputs.registry }}/$ECR_REPOSITORY:$IMAGE_TAG
- name: Print ECR image URL
if: github.ref == 'refs/heads/master'
run: echo ${{ steps.login-ecr.outputs.registry }}/$ECR_REPOSITORY:$IMAGE_TAG
We can read the workflow as follows:
You now should have successfully pushed your Next.js image to ECR and can begin using it in your Kubernetes environment! (part 2 to come)
Search Engine Optimization Part 1: How Does It Work?
Kai Cockrell
5th July 2024
Weights and Biases Website Design Review
David Hazra
6th June 2024
Aerospace Materials: A Journey Through Time
Daniel Bevan
10th May 2024
Visual Storytelling in Web Design
Daniel Bevan
27th April 2024
Visual Hierarchy: Controlling Your Users Focus
Kai Cockrell
19th April 2024
Gestalt Principles: Capturing Attention in Web Design
Kai Cockrell
12th April 2024
Linguistics and LLMs: Understanding Language
Kai Cockrell
11th April 2024
Databases on Kubernetes (Part 1)
David Hazra
10th April 2024
Inside UK Chip Designer ARM's Soaring Share Price
Daniel Bevan
19th February 2024
© 2024 White Crab Systems LTD. All rights reserved.