DEV Community

Cover image for Setting Up a VPS with Nginx, Docker, and GitHub Actions
Mohamed Gallah
Mohamed Gallah

Posted on

Setting Up a VPS with Nginx, Docker, and GitHub Actions

Intro

In this article, we'll be walking through setting up a VPS using Nginx, Docker and Github Actions.
This setup will allow you to deploy your web projects seamlessly as soon as you push code to Github repos.

We'll be covering up the following:

  1. Setting up the VPS
  2. Installing and configuring Nginx
  3. Installing Docker and setting up your environment.
  4. Setting up GitHub Actions for continuous deployment.
  5. Setting up SSL certificates with Let’s Encrypt and auto-renewal.

Pre-requisites

Before we get started, you'll need the following:

  1. A VPS with root access. I'll be using a VPS from RackNerd (but you can use any provider: Hertzner, DigitalOcean, AWS ...).
  2. A domain name. I'll be using a domain from Namecheap (required for SSL setup).
  3. Basic knowledge of SSH, Linux commands, and GitHub.

Setting up the VPS

For this article, I’ll assume you’re using Ubuntu as the OS.

  1. SSH into your VPS using the root user.
ssh root@your_vps_ip
Enter fullscreen mode Exit fullscreen mode
  1. Update the package list and upgrade the packages.
sudo apt update && sudo apt upgrade -y
Enter fullscreen mode Exit fullscreen mode

Installing and configuring Nginx

  1. Install Nginx.
sudo apt install nginx -y
Enter fullscreen mode Exit fullscreen mode
  1. Start and enable Nginx.
sudo systemctl start nginx
sudo systemctl enable nginx
Enter fullscreen mode Exit fullscreen mode
  1. Check the status of Nginx.
sudo systemctl status nginx
Enter fullscreen mode Exit fullscreen mode
  1. Open your browser and navigate to your VPS IP address. You should see the Nginx welcome page.

  2. Configure Nginx to serve your web projects. Create a new configuration file for your project.

sudo nano /etc/nginx/sites-available/default
Enter fullscreen mode Exit fullscreen mode

Update the configuration with your domain:

server {
    listen 80;
    server_name yourdomain.com www.yourdomain.com;

    location / {
        proxy_pass http://localhost:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
Enter fullscreen mode Exit fullscreen mode
  1. Save the file and exit the editor. Test the Nginx configuration.
sudo nginx -t
Enter fullscreen mode Exit fullscreen mode
  1. If the test is successful, reload Nginx.
sudo systemctl reload nginx
Enter fullscreen mode Exit fullscreen mode

Installing Docker

(From Docker's official documentation)

  1. Setup Docker's apt repo:
# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

# Add the repository to Apt sources:
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
Enter fullscreen mode Exit fullscreen mode
  1. Install Docker:
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Enter fullscreen mode Exit fullscreen mode
  1. Verify the installation:
sudo docker --version
docker-compose --version
Enter fullscreen mode Exit fullscreen mode

Setup GH Actions

In your project's root, create a Dockerfile and a docker-compose.yml file.

FROM node:14
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "start"]
Enter fullscreen mode Exit fullscreen mode
version: '3'
services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      NODE_ENV: production
Enter fullscreen mode Exit fullscreen mode

Now, create a .github/workflows/deploy.yml file in your project's root.

name: Deploy to VPS

on:
  push:
    branches:
      - main

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout repository
        uses: actions/checkout@v2

      - name: Build Docker image
        run: |
          docker build -t your-app:latest .
          docker save your-app:latest > your-app.tar
          chmod 664 your-app.tar

      - name: Transfer Docker image to VPS
        uses: appleboy/scp-action@master
        with:
          host: ${{ secrets.VPS_HOST }}
          username: ${{ secrets.VPS_USERNAME }}
          key: ${{ secrets.VPS_SSH }}
          port: ${{ secrets.VPS_PORT }}
          source: "your-app.tar"
          target: "/tmp"

      - name: Deploy to VPS
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.VPS_HOST }}
          username: ${{ secrets.VPS_USERNAME }}
          key: ${{ secrets.VPS_SSH }}
          port: ${{ secrets.VPS_PORT }}
          script: |
            docker load < /tmp/your-app.tar
            docker stop your-app || true
            docker rm your-app || true
            docker run -d --name your-app -p 8003:8003 your-app:latest
Enter fullscreen mode Exit fullscreen mode

Important 1: Replace your-app with your actual values.

Important 2: Add your VPS IP, ssh key and username as secrets in your GitHub repo. You'll need to redo this step with each new repo.

Testing the Deployment

Push a new commit to the main branch, and GitHub Actions should automatically build and deploy your application to your VPS.

Setting up SSL

We’ll use Let’s Encrypt, a free SSL/TLS certificate authority, to get a free certificate and set up auto-renewal using `certbot.

  1. Install Certbot with Nginx plugin:

bash
sudo apt install certbot python3-certbot-nginx -y

  1. Generate the SSL certificate:

bash
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com

With Nginx plugin installed earlier, Certbot will configure Nginx to use the SSL certificate and set up redirects from HTTP to HTTPS. You’ll be prompted to enter your email address and agree to the terms of service.

  1. Verify SSL installation:

Once Certbot completes the process, you can verify that your site is running with HTTPS by visiting https://yourdomain.com.

  1. Set up auto-renewal:

Certbot will automatically set up a cron job to renew your SSL certificate. You can verify this by running:

bash
sudo crontab -l

You should see a cron job similar to the following:

bash
0 */12 * * * certbot renew --quiet

Setup projects after deployment

After deploying your projects, you need to update your nginx configuration to point to the new containers.

First of all, make sure to change port used by your apps to avoid conflicts.

Now, given you deployed your app with the name your-app-container, using the port 8002, you can update your nginx configuration to point to the new container.

nginx
server {
# Your previous configuration
# ...
location /your-app-container {
rewrite ^/your-app-container/(.*) /$1 break;
proxy_pass http://localhost:8002;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# ...
# SSL configuration
}

Conclusion

You now have a fully automated deployment pipeline for your projects. Every time you push a new commit to the main/master branch, GitHub Actions will build and deploy your application to your VPS. You also have an SSL certificate installed on your server, which will automatically renew every 90 days.

Next Steps

You can take this basic setup further by adding more features like:

  • Setup an advanced Nginx configurtation to handle multiple domains and subdomains or complex load balancing.
  • Use Coolify or similar to create a vercel like deployment pipeline.
  • Adding firewall rules for additional security.

See ya! 🚀


Cover image: Photo by Kvistholt Photography on Unsplash

Top comments (0)