CI Guide: GitHub Actions and Digital Ocean
Node.js Full-Stack Application Deployment on Digital Ocean
The focus of this article will be on establishing an efficient GitHub CI pipeline that automates the deployment process. You’ll learn how to pull the latest changes from your main branch, rebuild your application — be it React, Node.js, or a combination of both — and manage the PM2 process on a DigitalOcean droplet.
Notably, this guide addresses the creation and usage of two SSH key pairs: one for general GitHub operations and another as a Deploy Key for secure deployment interactions. By adhering to best practices in SSH key management, this tutorial ensures a secure and functional setup, avoiding common pitfalls like unnecessary exposure of private keys.
Throughout the guide, we’ll employ the following representative details:
- DigitalOcean Username (the one we’ll create):
deployer
- Your main sudo username on the droplet:
other_sudo_user
- Application name & root directory of app:
my_app
- Your droplet’s IP:
DROPLET_IP
Step 1: Create a new user and grant sudo privileges
Step 2: Configure GitHub SSH Access
- Switch to the
deployer
user:su deployer
- Create two SSH Key Pairs:
Generate SSH Key Pair #1 (Personal Key): For general Git operations.
ssh-keygen -t rsa -b 4096 -C "your_email@example.com@my-macbook"
- Save to a unique path:
/home/deployer/.ssh/app_key
- If the
.ssh
directory doesn't exist:sudo mkdir /home/deployer/.ssh
- Display and copy the generated public key:
cat ~/.ssh/app_key.pub
- Add to GitHub Account: Paste the public key here.
Generate SSH Key Pair #2 (Deploy Key): Specifically for deployment.
ssh-keygen -t rsa -b 4096 -C "deploy_key_my_app"
- Save to a unique path:
/home/deployer/.ssh/deploy_key_my_app
- Set permissions:
chmod 600 ~/.ssh/deploy_key_my_app
- Display and copy the public key:
cat ~/.ssh/deploy_key_my_app.pub
- Add as Deploy Key: Navigate to your GitHub repo’s settings, select “Deploy keys”, click “Add Deploy Key”, and paste the public key.
Step 3. Append Public Keys to Authorized Keys
Add SSH Keys to Authorized Keys:
- Personal key:
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
- Deploy Key:
cat ~/.ssh/deploy_key_my_app.pub >> ~/.ssh/authorized_keys
Set permissions
- For
authorized_keys
:chmod 600 ~/.ssh/authorized_keys
- For
.ssh
directory:chmod 700 /home/deployer/.ssh
This setup ensures:
- Both personal and deploy keys enable SSH access to the server.
authorized_keys
is secure and well-configured.- The deploy key’s private key remains safely stored.
- The
.ssh
directory has optimal permissions for security.
Step 4: Set Up SSH for Secure Remote Access
Add GitHub’s RSA Host Key:
ssh-keyscan -t rsa github.com >> ~/.ssh/known_hosts
- This step helps your server recognize and trust GitHub for SSH connections.
Change Ownership of the SSH
directory:
sudo chown deployer:deployer /home/deployer/.ssh
Enable Password-less PM2 Commands:
- Edit the sudoers file:
sudo visudo
- Add:
deployer ALL=(other_sudo_user) NOPASSWD: /path/to/pm2
- Replace
/path/to/pm2
with the actual path to PM2 on your droplet, andother_sudo_user
with your primary sudo username.
Step 5: Change Ownership of the App Directory
Transfer Directory Ownership:
cd /home/other_sudo_user && sudo chown -R deployer: my_app
Create a Symbolic Link:
- Purpose: To simplify access to your app directory directly from the
deployer
user's home directory. - Command:
ln -s /home/other_sudo_user/my_app /home/deployer/my_app
- This link allows you to refer to
/home/deployer/my_app
instead of the longer path/home/deployer/home/other_sudo_user/my_app
, simplifying file management and access.
Manage PM2 Processes:
- If a PM2 process is running under your old sudo user, stop it.
- Start a new PM2 process under the
deployer
user. This aligns the process management with your new deployment workflow.
Step 6: Set Up the GitHub Repository Workflow
Define secrets in your repository’s settings:
- Add secrets here
DO_SSH_KEY
: SSH private key for the deploy key.DO_HOST
: Your droplet's IP.DO_USERNAME
: Username created (deployer
).
Create a CI Workflow File:
- Navigate to your repo, select Actions, and create a new workflow file.
Create a new workflow file. You can use the same one I use and tailor it for your specific use case:
Key Workflow Components:
on/push
: Triggers on any push to the main branch. For manual triggers, useworkflow_dispatch
strategy/matrix
: Specify Node.js versions. Example:18.x
Deploy to DigitalOcean: This step does the actual deployment work.
- Sets up the SSH key and permissions.
- Uses
scp
to transfer application code to the droplet. - Connects via SSH into the droplet to build UI and manage PM2.
- Runs:
cd ~/my_app/server && npm run build:ui && pm2 restart 0
- Cleans up by removing the temporary private key file.
Note:
npm run build:ui
is a custom script in my app server’spackage.json
that handles several tasks: it goes to the client directory, builds the UI, transfers the build to the server, and thenpm2 restart 0
restarts the PM2 process initiated by thedeployer
user.
Step 7: Commit and Push Changes
- Finalize Your Setup: Commit and push your changes. Monitor the GitHub Actions tab to observe your CI pipeline.
- Outcome: Each push to the main branch triggers an automated deployment to your DigitalOcean server, seamlessly updating your React/Node.js application.