Introduction
In today’s fast-paced development environment, automating the deployment process is crucial for maintaining efficiency and reliability. GitHub Actions provides a powerful platform for automating workflows, including deployment to Virtual Private Servers (VPS). In this article, we will explore how to set up GitHub Actions to automate your VPS deployment process, ensuring a seamless CI/CD experience.
While this method can be applied to various VPS providers and various software stacks, we will focus on a common scenario: deploying a Next.js application to a VPS running Ubuntu.
Prerequisites
Before we dive into the setup, ensure you have the following:
- A VPS running Ubuntu (or any other Linux distribution).
- SSH access to your VPS.
- A GitHub repository containing your Next.js application.
- A domain name pointing to your VPS (optional but recommended).
Step 1: Setting Up Your VPS
First, you need to prepare your VPS for deployment. Connect to your VPS via SSH:
ssh user@your-vps-ip
Once connected, update your package list and install Node.js and npm. We recommend using Node Version Manager (nvm) for managing Node.js versions:
# Step 1: Update the system
sudo apt update && sudo apt upgrade -y
# Step 2: Install prerequisites
sudo apt install curl build-essential libssl-dev -y
# Step 3: Install nvm
wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
# Load nvm
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"
# Step 4: Install the latest version of node
nvm install node
# Step 5: Check the installed versions
echo "Node version:"
node -v
echo "NPM version:"
npm -v
# Finish
echo "Installation completed!"
# Step 6: Install LTS version of node
nvm install --lts
nvm use --lts
The above commands will install the latest version of Node.js and npm. You may also want to install pm2
, a process manager for Node.js applications:
sudo npm install -g pm2
pm2
will help you manage your application processes and keep them running in the background, restarting them if they crash.
Step 2: Configuring Your GitHub Repository
2.1: Adding Secrets
To securely connect to your VPS from GitHub Actions, you need to add your VPS credentials as secrets in your GitHub repository. Go to your repository on GitHub, click on Settings, then Secrets and variables, and finally Actions. Here, you will add the following secrets:
HOST
: Your VPS IP address.USERNAME
: Your SSH username.SSH_PRIVATE_KEY
: Your private SSH key (make sure to copy the entire key, including the-----BEGIN OPENSSH PRIVATE KEY-----
and-----END OPENSSH PRIVATE KEY-----
lines).APP_DIR
: The directory where your application is located on the VPS.
If you don’t already have an SSH key, you can generate one using the following command:
cd ~/.ssh
ssh-keygen -t rsa -b 4096 -C "youremailaddress"
- The -t flag is for specifying type of key. Here we are generating RSA key
- The -b flag sets the key length, which determines the key’s strength. Here we are generating 4096 bit key
- The -c flag is is used to add a comment or label to the key. It’s not required but can be helpful for identifying the key’s purpose or owner.
You’ll be prompted to name your key. Let’s call it github_action
and you can leave the passphrase empty.
In order to copy the private key to your GitHub secrets, run the following command:
cat github_action
Copy the output and paste it into the SSH_PRIVATE_KEY
secret in your GitHub repository.
Make sure to keep your private key secure and never share it publicly.
Add the other GitHub secrets as well.
2.2: Adding the Public Key to Your VPS
After generating the key, you need to add the public key to your VPS’s ~/.ssh/authorized_keys
file:
cd ~/.ssh
cat github_action.pub >> authorized_keys
This command appends your public key to the list of authorized keys on your server, granting access for automated deployments.
Step 3: Creating the GitHub Actions Workflow
Create a GitHub Actions workflow by adding an action YAML file inside the .github/workflows
directory in your repository. You can name it deploy.yml
. In this file, you will define the steps for your deployment process. Namely, on an event such as pushing to the main branch, the workflow will build your application and deploy it to your VPS.
Here’s a sample workflow configuration:
name: Deployment Workflow
# Trigger this workflow on pushes to the specified branch
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Install dependencies
run: npm install
- name: Build Next.js app
run: npm run build
- name: SSH Deploy
# Use the 'appleboy/ssh-action' action for SSH deployment
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.HOST }} # Your server's IP address
username: ${{ secrets.USERNAME }} # Your server's username
key: ${{ secrets.SSH_PRIVATE_KEY }} # Your server's SSH private key
script: |
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
cd ${{ secrets.APP_DIR }} # Specify the path to your app directory on the server
git pull
npm install
npm run build
pm2 restart {service_name} # Replace with your PM2 service name
Note: Make sure to replace {service_name}
with the actual name of your PM2 service. In the script
section, you can customize the commands to fit your deployment needs. You’ll notice that we are running the nvm.sh script to load nvm before executing any Node.js commands. This is necessary due to the way the listed ssh-action runs commands in a non-interactive shell, which does not load the user’s profile. Thus, nvm will not be available unless we explicitly load it.
Step 5: Setting Up PM2
To ensure your application runs continuously, you can set it up with PM2. To run your application with PM2, navigate to your application directory and run:
pm2 start npm --name "nextjs" -- start
where "nextjs"
is the name you want to give your PM2 process. You can provide additional options as needed:
pm2 start npm --name "nextjs" -- start -- --port 3000
Finally, You can also set it to restart automatically on server reboots:
pm2 startup
pm2 save
The commands above will generate a startup script and save the current process list, ensuring that your application starts automatically when the server reboots.
Now that we have our application running with PM2, let’s explore how to handle errors, set up monitoring, and troubleshoot common issues in the following sections.
Error Handling and Rollback Strategies
When automating deployments, it’s crucial to have a plan for when things go wrong. Here are some strategies to handle errors and rollback if necessary:
1. Use PM2 for Zero-Downtime Deployments
PM2 supports zero-downtime reloads, which can help minimize the impact of a failed deployment:
pm2 reload {service_name}
2. Implement Version Tagging
Before each deployment, tag your Git commit with a version number:
git tag -a v1.0.0 -m "Version 1.0.0"
git push origin v1.0.0
If a deployment fails, you can easily rollback to a previous version:
git checkout v0.9.9
pm2 reload {service_name}
Monitoring and Logging
Monitoring your deployed application is crucial for maintaining its health and performance. Here are some basic steps to set up monitoring and logging:
1. PM2 Monitoring
PM2 provides built-in monitoring capabilities. You can view basic metrics with:
pm2 monit
For more detailed monitoring, consider setting up PM2 Plus, which offers a web-based dashboard.
2. Application Logs
PM2 also manages your application logs. View your application logs with:
pm2 logs {service_name}
To set up log rotation and prevent your logs from consuming too much disk space, use:
pm2 install pm2-logrotate
Troubleshooting
Here are some common issues you might encounter during setup or deployment, and how to resolve them:
1. SSH Connection Issues
If GitHub Actions can’t connect to your VPS, check the following:
- Ensure your VPS IP address is correct in the GitHub secrets.
- Verify that the SSH key is correctly added to your VPS’s authorized_keys file.
- Check if your VPS firewall is blocking incoming SSH connections.
2. Node.js Version Mismatch
If you encounter Node.js version-related errors, make sure that:
- The Node.js version on your VPS matches the one specified in your package.json.
- NVM is correctly loaded in your deployment script.
3. PM2 Process Not Found
If PM2 can’t find your process during restart, it might be because the process name has changed. You can list all PM2 processes with:
pm2 list
Then, update your deployment script with the correct process name.
4. Build Failures
If your build fails during deployment:
- Check if all required environment variables are set.
- Ensure all dependencies are correctly listed in your package.json.
- Review your application logs for any specific error messages.
Remember, when troubleshooting, the GitHub Actions logs and your application logs on the VPS are your best friends. Always check these first when something goes wrong.
Conclusion
Automating your VPS deployment process with GitHub Actions can significantly streamline your workflow, reduce human error, and ensure consistent deployments. By following the steps outlined in this guide, you’ve set up a robust CI/CD pipeline that automatically builds and deploys your Next.js application to your VPS whenever you push changes to your main branch.
This setup not only saves time but also allows for rapid iteration and seamless collaboration among team members. With the power of GitHub Actions and the flexibility of VPS hosting, you can focus more on developing features and less on the intricacies of deployment.
Remember, while this guide focused on deploying a Next.js application to an Ubuntu VPS, the principles can be adapted for various tech stacks and hosting environments. As you become more comfortable with this workflow, consider exploring additional features like running automated tests before deployment or setting up staging environments.
By embracing automation in your deployment process, you’re taking a significant step towards more efficient and reliable software development practices. Happy coding and deploying!