Setting up CI/CD for your Node.js app using GitHub Actions can save time and reduce errors by automating repetitive deployment tasks. In this guide, you’ll learn to configure a workflow that pulls code, installs dependencies, manages SSH securely, and deploys with PM2.
What You’ll Need
- GitHub Repository: For hosting your Node.js code.
- VPS: A server with SSH access to deploy your app.
- Secrets Setup: Add credentials (SSH key, IP, user, and environment variables) in GitHub Secrets for secure access.
GitHub Actions Workflow
Here’s a full YAML configuration with each step explained:
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
# 1. Checkout Code
- name: Checkout code
uses: actions/checkout@v3
# 2. Set Up Node.js
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
# 3. Install Dependencies
- name: Install dependencies
run: npm ci
# 4. Set Up SSH for Deployment
- name: Set up SSH key
uses: webfactory/ssh-agent@v0.8.0
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
# 5. Add VPS to Known Hosts
- name: Add VPS to known hosts
run: |
mkdir -p ~/.ssh
ssh-keyscan -H "${{ secrets.VPS_IP }}" >> ~/.ssh/known_hosts
# 6. Deploy Files to VPS
- name: Deploy to temporary directory on VPS
env:
VPS_USER: ${{ secrets.VPS_USER }}
run: |
rsync -avz --delete ./ ${VPS_USER}@${{ secrets.VPS_IP }}:/tmp/node-hello/
# 7. Move Files to Final Directory on VPS
- name: Move files to /root/node-hello
env:
VPS_USER: ${{ secrets.VPS_USER }}
run: |
ssh ${VPS_USER}@${{ secrets.VPS_IP }} "
sudo rsync -avz /tmp/node-hello/ /root/node-hello/
"
# 8. Set Up Environment and Manage PM2
- name: Configure environment and manage PM2
env:
VPS_USER: ${{ secrets.VPS_USER }}
MONGO_URI: ${{ secrets.MONGO_URI }}
JWT_SECRET: ${{ secrets.JWT_SECRET }}
BCRYPT_SALT_ROUNDS: ${{ secrets.BCRYPT_SALT_ROUNDS }}
PORT: ${{ secrets.PORT }}
NODE_ENV: 'production'
run: |
ssh ${VPS_USER}@${{ secrets.VPS_IP }} "
sudo bash -c '
cd /root/node-hello
# Write environment variables to .env
cat > .env <<EOL
MONGO_URI=${MONGO_URI}
JWT_SECRET=${JWT_SECRET}
BCRYPT_SALT_ROUNDS=${BCRYPT_SALT_ROUNDS}
NODE_ENV=${NODE_ENV}
PORT=${PORT}
EOL
# Check for PM2 and start app
if ! command -v pm2 &> /dev/null; then
npm install -g pm2
fi
pm2 startOrRestart server.js --name \"server\" --update-env
'
"
# 9. Clean Up Known Hosts
- name: Clean up known hosts
run: |
ssh-keygen -R "${{ secrets.VPS_IP }}"
Workflow Breakdown
- Checkout Code: Pulls your latest code from the main branch.
- Node.js Setup: Sets up Node.js (version 18 or higher) for compatibility with modern libraries.
- Install Dependencies:
npm ci
is preferred overnpm install
for faster installs and consistency withpackage-lock.json
. - SSH Key Setup: Uses
webfactory/ssh-agent
to securely handle SSH. This action adds your SSH key to the agent without saving it locally. - Add VPS to Known Hosts: Adds your server’s IP to the SSH known hosts list, avoiding interactive prompts during SSH.
- Deploy Files to VPS: Uses
rsync
to deploy files to a temporary directory on the VPS, ensuring only the latest files are uploaded. - Move Files to Final Directory: Moves files from the temporary to the final directory, requiring root privileges to write to
/root/node-hello
. - Set Environment and PM2: Writes environment variables to a
.env
file on the server, then starts or restarts the app using PM2. ThestartOrRestart
command is efficient and reduces downtime. - Clean Up Known Hosts: Removes the VPS IP from known hosts after deployment. This step prevents clutter and mitigates risks from SSH fingerprint changes.
Warning! This is a beginner setup and may not cover all scenarios. We’re open to suggestions and feedback to improve this guide, so feel free to share any ideas or tips you have.