Joseph Thomas
Posted on August 26, 2020
I spent the afternoon wrestling with getting some Github actions set up for CI using pm2. I had some issues & confusion around SSH issues, and couldn't find anything online, so I wanted to post this here --- even if it's just for me to refer to later. In particular, I was dealing with Host key verification failed.
when attempting to deploy from the Github action.
If you're not familiar with PM2, it's a process manager that will run your node.js apps, allowing you to stop, restart, and view logs.
I'm deploying to a DigitalOcean droplet, but the following should be the same for any other VPS. Here are a few tutorials you can follow to recreate the same setup:
Once you have your server set up and your repository hosted on Github, follow these steps:
1. Set up PM2 configuration
Create or update ecosystem.config.js
. Mine looks like this:
module.exports = {
apps: [
{
name: 'myapp-api',
script: 'yarn start:api',
time: true,
instances: 1,
autorestart: true,
max_restarts: 50,
watch: false,
max_memory_restart: '1G',
env: {
PORT: 3000,
DATABASE_ADDRESS: process.env.DATABASE_ADDRESS
},
},
],
deploy: {
production: {
user: 'username',
host: '165.232.50.103',
key: 'deploy.key',
ref: 'origin/main',
repo: 'https://github.com/username/myapp',
path: '/home/username/myapp',
'post-deploy':
'yarn install && yarn build && pm2 reload ecosystem.config.js --env production && pm2 save && git checkout yarn.lock',
env: {
NODE_ENV: 'production',
DATABASE_ADDRESS: process.env.DATABASE_ADDRESS
},
},
},
}
Make sure you aren't missing the deploy.production.key
value, and that your ref
matches the branch you want to deploy from on github. (I've renamed my master
branch to main
, see npx no-masters)
2. Create a SSH key pair
To get things to work, we need to be able to ssh into our remote server from the machine running the github action. Our next step is to create some new SSH keys. (You could use ones you already have, but it's safer to create new ones just for this project).
On your local machine, run:
ssh-keygen -t rsa -b 4096 -C "username@SERVER_IP" -q -N ""
When prompted about the file location, enter gh_rsa
, or anything else you'd like - we will delete these files after getting the information we need from them.
3. Remote server setup
- Copy the contents of the public file:
cat gh_rsa.pub | pbcopy
(or if you don't havepbcopy
, justcat
and then copy from the terminal output). - SSH into your remote server:
ssh username@SERVER_IP
- Add the public key to your user's
authorized_keys
:
echo "ssh-rsa AAAA....YOUR_PUBLIC_KEY..." >> ~/.ssh/authorized_keys
4. Github Secrets setup
Next, we'll add some secrets to the Github repo for use in the action. Go to your repo's Settings > Secrets pane, then add two new keys:
- Secret key:
-
cat gh_rsa | pbcopy
to copy the private key to your clipboard - In Github, create a new secret named
SSH_PRIVATE_KEY
and paste in the contents.
-
- Known hosts:
ssh-keyscan SERVER_IP > pbcopy
- In Github, create a new secret named
SSH_KNOWN_HOSTS
and paste in the contents.
5. Github Action configuration
Lastly, on your local machine, create or update the file .github/workflows/main.yml
(or whatever file you are using for your action):
name: CI - Master
on:
push:
branches: [ main ]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
#
# ... your other steps, such as running tests, etc...
#
- name: Set up SSH
run: |
mkdir -p ~/.ssh/
echo "$SSH_PRIVATE_KEY" > ./deploy.key
sudo chmod 600 ./deploy.key
echo "$SSH_KNOWN_HOSTS" > ~/.ssh/known_hosts
shell: bash
env:
SSH_PRIVATE_KEY: ${{secrets.SSH_PRIVATE_KEY}}
SSH_KNOWN_HOSTS: ${{secrets.SSH_KNOWN_HOSTS}}
# (optional - only needed if your config uses environment variables)
- name: Create env file
run: |
touch .env
echo DATABASE_ADDRESS=${{ secrets.DATABASE_ADDRESS }} >> .env
- name: Install PM2
run: npm i pm2
- name: Deploy
run: env $(cat .env | grep -v \"#\" | xargs) pm2 deploy ecosystem.config.js staging
# Or alternately, put this deploy script in your package.json's scripts and run it using yarn/npm:
# run: yarn deploy
The key steps here are:
-
Set up SSH: this creates the
deploy.key
file, so when pm2 deploys, it matches the public key that we added to the server'sauthorized_keys
. -
Create Env File: If your PM2 configuration uses environment variables, we need to use our Github secrets to populate
process.env
with these variables. In our "Deploy" step, we use theenv
command to load the file we created.
6. Cleanup & security
- Add
deploy.env
to your.gitignore
file. - Delete the
gh_rsa
andgh_rsa.pub
files that we created. Most importantly: if you created your SSH keys within your project's directory, be sure to not commit them.
Conclusion
That should do it! If you have any issues, please leave a comment and I'll get back as soon as I can. :)
Posted on August 26, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.