Using GitHub Actions to deploy a web page to Raspberry Pi
Tags: devops, github, raspberrypi
Published: 2022-05-03 22:11:12

A couple of months ago I bought a Raspberry Pi 4. Since then, I've been getting my hands dirty w/ SSH, SCP, Systemd, DDNS and some other things I've never done before. This machine now hosts my [Veloren](https://veloren.net) server (Join me at [vlrn.duckdns.org](vlrn.duckdns.org)) and a landing page which, after manually building and SCP'ing a lot, is now deployed through GitHub Actions. And this is today's topic: How to deploy a webpage to a RPi using GH Actions?

# Before starting
If you have a dynamic IP address, you'll have to setup a Dynamic DNS, otherwise, you'd have to change the target IP address every time yours change (It can happen monthly, weekly or even daily). This won't be covered here, since the process differs based on your Router model. However, if you want a good and free Dynamic DNS service, consider using (and donating!) to [duckdns.org](duckdns.org) which is the one I currently use. 

# Preparing the RPi
In this case, preparing is pretty straightforward, you just need to install NGINX using:
```sh
apt install nginx
```
And then start it:
```sh
systemctl enable nginx
systemctl start nginx
```
## Port forwarding

Port forward RPi's port 80 to external port 80. Now, when accessing the IP from a browser, this screen will appear:

![Welcome to nginx! If you see this page, the nginx web server is successfully installed and working. Further configuration is required. For online documentation and support please refer to nginx.org. Commercial support is available at nginx.com. Thank you for using nginx.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3dy848lg3y95gnau72jf.png)
</div>

Also, port-forward the port 22 of the RPi to **anything other than** 22. This is the default SSH port, so it's better not to expose it for security reasons.

# Creating the Action

This is a pretty simple step, because of the great [appleboy/scp-action](https://github.com/appleboy/scp-action/issues). The action will have only one job with two steps: Building and pushing to Raspberry Pi.

## Building

There's no secret here, this is the build step:
```yml
name: deploy
on:
  push:
    branches:
      - main
jobs:
  deploy:
    name: Deploy
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: "14"
      - name: "Install & build"
        run: |
          npm i
          npm run build
          mv public/index.html public/index.nginx-debian.html
```
The only thing to be aware here is the last line, where `index.html` gets renamed to `index.nginx-debian.html`. This is done because on Debian, NGINX uses `/var/www/html/index.nginx-debian.html`. Be aware that other distros can use a different directory and index naming.

## Pushing 

To push the recently-built artifact to the Raspberry Pi, we'll use [appleboy/scp-action](https://github.com/appleboy/scp-action). To correctly set it up, add the following secrets to your GitHub repo:

- **SSH_HOST:** IP/URL of the RPi;
- **SSH_PORT:** The SSH port used by the RPi, which was forwaded in the [Port Forwarding](#port-forwarding) section;
- **SSH_USERNAME:** The RPi username to SCP to. Since the files will be copied to `/var/www/html`, this user should have root privileges, preferrably without asking for password;
- **SSH_KEY:** An SSH **Private** Key, generate it using either `ssh-keygen` or an [online generator](https://8gwifi.org/sshfunctions.jsp).

Once you have everything created, add the SSH **Public** key to `~/.ssh/authorized_keys`. Now it's time to create the "Push" step:

```yml
      - name: "Push to server"
        uses: appleboy/scp-action@master
        with:
          host: ${{ secrets.SSH_HOST }}
          username: ${{ secrets.SSH_USERNAME }}
          key: ${{ secrets.SSH_KEY }}
          port: ${{ secrets.SSH_PORT }}
          source: "public/"
          strip_components: 1
          target: "/var/www/html"
```

There shouldn't be anything new other than `strip_components: 1`. This 
setting removes the specified number of leading path elements, without it, we'd push the content to `/var/www/html/public` instead of `/var/www/html`.

## The end
Now you should have a fully functional GitHub action that automatically builds and pushes to a Raspberry Pi. No more SCP'ing for changing a single line or running `npm run build` inside the RPi. You can see the repo for my website here:
{% embed https://github.com/hnrq/vlrn-website %}