🚀 Deploy Next.js with Docker, Traefik, SSL, and CI/CD
Aug 2, 2025 • 4 min read

This tutorial walks you through deploying a production‑ready Next.js application using Docker and Docker Compose on a fresh cloud server. You’ll set up a reverse proxy with Traefik, enable automatic SSL via Let’s Encrypt, and wire up a basic Jenkins CI/CD pipeline for tagged deployments.
What You Will Achieve
- Choose a cloud server and set it up with Docker and Docker Compose
- Create a production‑ready Dockerfile for Next.js
- Configure Docker Compose with Traefik (reverse proxy) + automatic SSL
- Deploy via pushed Docker image or by building directly on the server
- Keep the app running automatically and view logs
- Configure a Jenkins pipeline to build/push images and deploy on tags
Server Comparisons: EC2 vs Droplet vs GCP
Provider | Ease of Use | Cost (approx. monthly) | Notes |
---|---|---|---|
Amazon EC2 | Moderate (AWS Console) | $8–$15 (t3.micro) | Rich features, more complex UI |
DigitalOcean Droplet | Very easy (simple UI) | $5–$15 | Developer‑friendly, predictable pricing |
Google Cloud (GCP) | Moderate | $7–$15 | Strong integrations, sometimes complex |
For this guide, we’ll use a DigitalOcean Droplet for simplicity and predictable pricing.
Step 1: Set up the server and install Docker + Compose
SSH into your server (Ubuntu 22.04 example):
ssh root@your_droplet_ip
Update and install prerequisites:
apt update && apt upgrade -y
apt install -y curl apt-transport-https ca-certificates software-properties-common
Install Docker:
curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh
usermod -aG docker ${USER}
newgrp docker
docker --version
Install Docker Compose v2:
mkdir -p ~/.docker/cli-plugins/
curl -SL https://github.com/docker/compose/releases/download/v2.18.1/docker-compose-linux-x86_64 \
-o ~/.docker/cli-plugins/docker-compose
chmod +x ~/.docker/cli-plugins/docker-compose
docker compose version
Step 2: Create an optimized Dockerfile for Next.js
Save this Dockerfile in your Next.js project root:
# Stage 1: Build
FROM node:20-alpine AS builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build
# Stage 2: Production image
FROM node:20-alpine
WORKDIR /app
ENV NODE_ENV=production
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/public ./public
COPY --from=builder /app/package.json ./package.json
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3000
CMD ["npm", "start"]
Key points:
- Multi‑stage build keeps the final image small
- Only production artifacts are copied to the runtime image
- App listens on port 3000
Step 3: Configure Docker Compose with Traefik + TLS
Create docker-compose.yml
in the project root:
version: "3.8"
services:
nextjs:
build: .
container_name: nextjs_app
restart: always
labels:
- "traefik.enable=true"
- "traefik.http.routers.nextjs.rule=Host(`example.com`)"
- "traefik.http.routers.nextjs.entrypoints=websecure"
- "traefik.http.routers.nextjs.tls.certresolver=myresolver"
- "traefik.http.services.nextjs.loadbalancer.server.port=3000"
networks:
- web
traefik:
image: traefik:v2.11
container_name: traefik_proxy
restart: always
command:
- "--api.insecure=true"
- "--providers.docker=true"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.myresolver.acme.httpchallenge=true"
- "--certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web"
- "--certificatesresolvers.myresolver.acme.email=your-email@example.com"
- "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
ports:
- "80:80"
- "443:443"
- "8080:8080" # Traefik dashboard (optional)
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "traefik_letsencrypt:/letsencrypt"
networks:
- web
networks:
web:
external: true
volumes:
traefik_letsencrypt:
Notes:
- Replace
example.com
andyour-email@example.com
with your domain/email - Traefik handles HTTPS automatically via Let’s Encrypt
- Create the shared
web
network first:
docker network create web
Step 4: Domain setup and SSL
- Point your domain A record to your server IP
- Verify DNS propagation (e.g.,
ping example.com
) - On first
docker compose up
, Traefik will request a TLS certificate
Step 5: Deployment strategies
Option A: Build & push a Docker image
docker build -t your-registry/nextjs-app:latest .
docker push your-registry/nextjs-app:latest
# On the server
docker pull your-registry/nextjs-app:latest
docker compose up -d
Option B: Build on the server
# Push code to the server (git/rsync/etc), then
docker compose up -d --build
Step 6: Keep it running and view logs
systemctl enable docker
docker logs -f nextjs_app
Step 7: Jenkins CI/CD pipeline (basic example)
pipeline {
agent any
environment {
REGISTRY = "your-registry/nextjs-app"
}
stages {
stage('Checkout') {
steps { checkout scm }
}
stage('Build and Push') {
steps {
script {
docker.build(env.REGISTRY + ":${env.GIT_TAG}").push()
}
}
}
stage('Deploy') {
steps {
sshagent(['server-ssh']) {
sh '''
ssh root@your_droplet_ip \
"docker pull ${env.REGISTRY}:${env.GIT_TAG} && docker-compose up -d"
'''
}
}
}
}
triggers { pollSCM('H/5 * * * *') }
post {
success { echo "Deployment successful for tag ${env.GIT_TAG}" }
failure { echo "Deployment failed" }
}
}
Summary: key commands
Task | Command/snippet |
---|---|
Create Docker network | docker network create web |
Build Docker image | docker build -t your-registry/nextjs-app:latest . |
Push Docker image | docker push your-registry/nextjs-app:latest |
Deploy with Compose | docker compose up -d or docker compose up -d --build |
Enable Docker on startup | systemctl enable docker |
View container logs | docker logs -f nextjs_app |
This end‑to‑end approach gives you a reproducible, secure deployment with HTTPS, easy rollouts via Compose, and a simple CI/CD pipeline to automate builds and releases.
Sources
Related articles


