Using Github Actions to Deploy Updates to my VPS
Tags: githubactions
Published: 2022-08-12 19:41:47

---
title: Using Github Actions to Deploy Updates to my VPS
published: true
description: A description of a simple but useful Github Action
tags: githubActions
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vfvz27yxgyici3eyrzwo.jpg
# Use a ratio of 100:42 for best results.
---

I self-host my [martingraham.dev](https://www.martingraham.dev) website on a VPS on [Linode](https://www.linode.com).  The source code is hosted on [github](https://github.com/martin-anderson-graham/martingraham-dot-dev), and so every time I wanted to committed updates I had to remote into my VPS to pull the latest version of the repo, build the site and restart the relevant processes.  What is this, 1985?  Enter [Github Actions](https://github.com/features/actions).

### Github actions introduction

Github actions are customizable automations that can be triggered by various github-related events.  In my case, my action occurs when a commit to my `main` branch occurs.

We define our actions in a `.yml` file located in `.github/workflows`.  Its worth saying at the top - this file will be visible in our repository, so we want to be careful about what information is exposed.  Security first!

```yml
name: Update Linode VPS Action
on: 
  push:
    branches:
      - 'main'
jobs:
  Push-to-linode:
    runs-on: ubuntu-latest
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
    steps:
      - name: Configure SSH
        run: |
          mkdir -p ~/.ssh/
          echo "$SSH_KEY" > ~/.ssh/staging.key
          chmod 600 ~/.ssh/staging.key
          cat >>~/.ssh/config <<END
          Host staging
            HostName $SSH_HOST
            User $SSH_USER
            IdentityFile ~/.ssh/staging.key
            StrictHostKeyChecking no
          END
        env:
          SSH_USER: ${{ secrets.STAGING_SSH_USER }}
          SSH_KEY: ${{ secrets.STAGING_SSH_KEY }}
          SSH_HOST: ${{ secrets.STAGING_SSH_HOST }}
      - name: Run npm run deploy
        run: |
          ssh staging 'cd ~/martingraham-dot-dev; npm run deploy'

```

Let's break this down:
```yml
on: 
  push:
    branches:
      - 'main'
```
This bit defines when the action will run.  Mine is super simple, but events are very configurable for advanced deployments.

A workflow can have multiple jobs - mine only has one, entitled `Push-to-linode`
```yml
jobs:
  Push-to-linode:
```

This conditional isn't doing anything (due to my event setup), but I left it in for my own future reference of the capability
```yml
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
```

I should mention the `runs-on: ubuntu-latest` line.  Github actions run on so-called runners.  In our case, the runner is operated by github.  Our later commands will be run from within this runner environment.  You can self-host your own runner, but that could potentially expose the machine to arbitrary code execution (say you have an action that runs on PRs or forks), and so hosting your own run is discouraged for public repos.
```yml
jobs:
  Push-to-linode:
    runs-on: ubuntu-latest
```


### SSH and secrets

Our job runs a few steps, the first of which is to configure an SSH connection to my VPS.  The `run` command executes command within the runner environment, so here we are creating an ssh configuration file.

```yml
run: |
  mkdir -p ~/.ssh/
  echo "$SSH_KEY" > ~/.ssh/staging.key
  chmod 600 ~/.ssh/staging.key
  cat >>~/.ssh/config <<END
  Host staging
    HostName $SSH_HOST
    User $SSH_USER
    IdentityFile ~/.ssh/staging.key
    StrictHostKeyChecking no
  END
```

`$SSH_USER` is a pretty weird user account to use on my VPS, no?  In fact, I don't want the whole internet to know the name of my VPSs internal user accounts.  And I certainly don't want them to have a copy of my private SSH key.  That's where `env:` comes in

```yml
env:
  SSH_USER: ${{ secrets.STAGING_SSH_USER }}
  SSH_KEY: ${{ secrets.STAGING_SSH_KEY }}
  SSH_HOST: ${{ secrets.STAGING_SSH_HOST }}
```

This sets up environment variable on the runner which can be accessed by my jobs.  In our Github repository we head into the settings and we can define secrets (SSH keys, API keys, etc) that are accessible by our Github Actions.  Apparently they are also are redacted in logs, but you should always be careful about not logging such values.

### NPM scripts

The next step uses my newly configured SSH connection to connect to my VPS and run commands.

```yml
- name: Run npm run deploy
  run: |
    ssh staging 'cd ~/martingraham-dot-dev; npm run deploy'
```

Notice `ssh staging` - if you look up above you will see that in the configuration file we named the target by writing `HOST staging`, and that is what is being referenced here.

In my case, I am running a node app ([SvelteKit](https://kit.svelte.dev/) to be precise) that requires a build step, and then the `pm2` process associated with the app has to be restarted.  I utilize an `npm script` to pull the latest version of the repo, build the app and restart the process.  I like this way fo doing it because if I need to change my build process I don't need to mess with my github action configuration.

### Conclusion

So there's a simple introduction to Github Actions - go and save yourself some steps!

Photo by <a href="https://unsplash.com/@agk42?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Alex Knight</a> on <a href="https://unsplash.com/s/photos/automation?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>