DEV Community

Cover image for How to deploy a Nextjs app with Kamal 2.4, AWS ECR, and Github Actions
Derick Zihalirwa
Derick Zihalirwa

Posted on • Edited on

How to deploy a Nextjs app with Kamal 2.4, AWS ECR, and Github Actions

Why Use Kamal for Deployment?

So, why pick Kamal for your deployments?

Simple: it makes the process smooth, fast, and reliable—without locking you into a complicated toolchain. Here’s what makes Kamal shine:

  • Readable YAML configs: Easy to write, easy to understand.
  • First-class Docker support: Get consistent builds wherever you deploy.
  • Rolling deployments: No downtime. No late-night fire drills.
  • Built-in health checks: Kamal keeps an eye on your app so you don’t have to.

Whether you're shipping fast or scaling up, Kamal helps you stay in control.


What You’ll Need Before You Start

Before jumping in, make sure your dev environment’s good to go:

  • An AWS account, with the CLI set up
  • Docker installed (and basic CLI comfort)
  • Kamal version 2.4 or higher
  • A working Next.js app
  • Some GitHub Actions knowledge

Step 1: Initialize Kamal in Your Project

Start by initializing Kamal in your app repo. Run:

kamal init 
Enter fullscreen mode Exit fullscreen mode

This sets up a .kamal directory with the core config files you’ll customize next.


Project Structure Overview

Here’s what your project directory should look like after setup:

project-root/ ├── .kamal/ # Kamal deployment config │ ├── deploy.yml # Main deployment settings │ └── secrets # Environment secrets ├── .github/workflows/ # CI/CD pipelines │ └── release-deploy.yml # GitHub Actions workflow ├── Dockerfile # Production build setup ├── package.json # Dependencies ├── yarn.lock # Lockfile ├── .env # Local environment variables ├── public/ # Static assets └── .next/ # Compiled Next.js output 
Enter fullscreen mode Exit fullscreen mode

Example deploy.yml for Kamal

Here’s a sample deploy.yml config tuned for staging:

defaults: service: web-app image: example/web-app servers: web: - 192.168.1.1 # Replace with your staging server IP ssh: user: ubuntu keys: ["~/.ssh/example-key.pem"] proxy: ssl: false app_port: 3000 host: stagingapp.example.io healthcheck: path: / interval: 3 timeout: 30 registry: server: 123456789012.dkr.ecr.us-west-1.amazonaws.com username: AWS password: <%= %x(aws ecr get-login-password) %> builder: arch: amd64 context: . env: clear: NEXT_PUBLIC_APP_BACKEND_URL: https://staging.api.example.io NEXT_PUBLIC_APP_PUBLIC_IP_API: https://pro.ip-api.com/json/?key=dummykey 
Enter fullscreen mode Exit fullscreen mode

Building a Dockerfile for Next.js

Here’s a lean, production-ready Dockerfile for a Next.js app:

FROM node:lts-bookworm-slim AS base FROM base AS deps WORKDIR /app COPY package.json yarn.lock ./ RUN yarn install --frozen-lockfile ARG NEXT_PUBLIC_APP_BACKEND_URL ARG NEXT_PUBLIC_APP_PUBLIC_IP_API ENV NEXT_PUBLIC_APP_BACKEND_URL=$NEXT_PUBLIC_APP_BACKEND_URL ENV NEXT_PUBLIC_APP_PUBLIC_IP_API=$NEXT_PUBLIC_APP_PUBLIC_IP_API COPY . . RUN yarn run build FROM base AS release WORKDIR /app ENV NEXT_TELEMETRY_DISABLED=1 COPY --from=deps /app/node_modules ./node_modules COPY --from=deps /app/package.json ./package.json COPY --from=deps /app/.next ./.next COPY --from=deps /app/public ./public EXPOSE 3000 CMD ["yarn", "start"] 
Enter fullscreen mode Exit fullscreen mode

This sets you up for reproducible builds and fast deployment.


Automating Deployments with GitHub Actions

Want your app to deploy automatically when a new release is published? Here’s a GitHub Actions workflow that gets the job done:

name: Release Deploy on: release: types: [published] permissions: contents: read packages: write id-token: write env: ECR_REPOSITORY: example/web-app jobs: package: runs-on: ubuntu-latest steps: - name: Checkout Repo uses: actions/checkout@v4 with: fetch-depth: 0 - name: Create .env file run: | echo "${{ secrets.PROD_ENV_FILE }}" > .env - name: Configure AWS uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} aws-region: us-west-1 - name: Login to ECR uses: aws-actions/amazon-ecr-login@v2 - name: Build & Push Docker Image uses: docker/build-push-action@v5 with: context: . file: ./Dockerfile platforms: linux/amd64 push: true tags: | ${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}:${{ github.sha }} 
Enter fullscreen mode Exit fullscreen mode

This pipeline checks out your code, configures AWS credentials, builds the Docker image, and pushes it straight to your ECR registry—all triggered by a GitHub release.


Wrapping Up

And there you have it: a solid deployment workflow using Kamal 2.4, Docker, and GitHub Actions.

You’ve got:

  • A clean, reproducible setup
  • Auto-deployments on release
  • A scalable, self-hosted Next.js app on AWS

No vendor lock-in. No drama. Just fast, predictable deployments.

Top comments (0)