How to deploy automatically Next Js App using Github actions, Docker, Nginx, Certbot
Hey folks. As an engineer, knowing how to properly deploy a project along with development has become mandatory these days. Today I will discuss how you can automatically build and deploy a next js project on any VPS server.
We’ll use GitHub Actions to automate the process, Docker for containerization, Nginx as our reverse proxy, and Certbot to handle SSL certificates automatically.
Prerequisites
- VPS or Server: You’ll need a Linux server with root access (e.g., Ubuntu 20.04).
- Domain: A registered domain to set up SSL with Certbot.
- Docker and Docker Compose: Installed on the server.
- Nginx: Installed on the server as a reverse proxy.
- GitHub Repository: Where your Next.js project is hosted.
- Docker Hub Account: To push Docker images.
First, create a Dockerfile in the root of your Next.js project:
FROM node:20 AS deps
WORKDIR /app
COPY package.json package-lock.json* ./
RUN npm ci
FROM node:20 AS builder
WORKDIR /app
COPY . .
COPY --from=deps /app/node_modules ./node_modules
RUN npm run build
FROM node:20 AS runner
WORKDIR /app
ENV NEXT_PUBLIC_NODE_ENV=production
COPY package.json package-lock.json ./
COPY --from=builder /app/next.config.mjs ./
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3000
CMD ["npm", "start"]
Then, we will generate SSH for auto-deployment from GitHub commits to the Ubuntu server with GitHub actions.
- local terminal ssh
cd .ssh
ssh-keygen -t rsa -b 4096 -C "youremail@gmail.com"
- Copy that key to add to the server
cat ~/.ssh/id_rsa.pub
- Add public key to .ssh on ubuntu server
cd /root/.ssh
nano authorized_keys
- Copy that private key to add to GitHub
cat ~/.ssh/id_rsa
- Add private key Github repo
Settings > Secrets > Actions > New repository secret. Label: PRIVATE_KEY
Next step install Nginx and Certbot in your VPS
sudo apt update
sudo apt upgrade
sudo apt install nginx certbot python3-certbot-nginx
sudo ufw allow "Nginx Full"
sudo ufw allow OpenSSH
sudo ufw enable
Configure Nginx to route traffic to the Docker container running Next.js:
- create a new config file for your domain
sudo nano /etc/nginx/sites-available/yourdomain.com
server {
server_name yourdomain.com www.yourdomain.com;
# Proxy requests to your Docker container running on port 3000
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/your-domain/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/your-domain/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
- Enable the Nginx Configuration
sudo ln -s /etc/nginx/sites-available/yourdomain.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx
After that, we can generate SSL for the domain
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
Go to your GitHub repository settings and set up the following secrets:
DOCKER_USERNAME
andDOCKER_PASSWORD
: Your Docker Hub credentials.
And in the last step create a deploy.yml in .github/workflows/ folder
name: Deploy Web App
on:
push:
branches:
- your-branch-name
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
push: true
tags: your-username/project:latest
- name: Set up SSH
uses: webfactory/ssh-agent@v0.5.1
with:
ssh-private-key: ${{ secrets.PRIVATE_KEY }}
- name: Deploy to VPS
env:
HOST: your vps ip address
USERNAME: root
DOCKER_IMAGE: your-username/project:latest
run: |
ssh -o StrictHostKeyChecking=no $USERNAME@$HOST << EOF
# Create Docker network if it doesn’t exist
docker network create app_network || true
# Pull the latest Docker image from Docker Hub
docker pull $DOCKER_IMAGE
# Stop and remove any existing container if it exists
if [ \$(docker ps -q -f name=docker-image-name) ]; then
docker stop docker-image-name
fi
if [ \$(docker ps -aq -f name=docker-image-name) ]; then
docker rm docker-image-name
fi
# Run the container with the required port mapping and environment variables
docker run -d --name docker-container-name --network app_network -p 3000:3000 \
-e NEXT_PUBLIC_NODE_ENV=production \
$DOCKER_IMAGE
EOF
You now have a fully automated deployment pipeline for your Next.js app! This setup ensures your app is deployed seamlessly upon each push to your selected branch, Docker and Nginx are properly configured, and SSL is handled by Certbot for secure connections.
Hope that will help you understand the general view of automated deployment. See you next article.