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
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
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
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"]
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 }}
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)