Hosting on Raspberry Pi

I am hosting my website on my Raspberry Pi.


The Idea

I recently finished my Portfolio App and had an exciting idea: Why not host it on my Raspberry Pi. It will be a good learning experience and I will finally be putting it to good use after neglecting it for a couple of years now.

Research and Prep

Like most newbies my first step was to go on Youtube for Guidance. I pretty much struck gold when I came across Fireship's Raspberry Pi versus AWS // How to host your website on the RPi4 as I knew it would be a helful and more importantly an entertaining guide.

Here are the rough steps I took in chronological order while setting everything up:

  • Install NGINX.
  • Set up the proxy config to send the traffic to my App. I did this by changing in NGINX config file.
proxy_pass http://localhost:3000;
  • Set up DDNS at NOIP.
  • Install the NOIP client on the raspberrypi to set up the DDNS record. I mainly followed their documentation which had detailed steps for installing in Raspberry Pi.
  • Install Certbot to manage SSL certificates.
  • Finally make a script to run the App and the NOIP command to check for IP Changes.
#!/bin/bash
 
cd portfolio
 
# Run the App
nohup npm run start > npm_output.log 2>&1 &
 
# Run NOIP
nohup noip-duc -g all.ddnskey.com --username $NOIP_USERNAME --password $NOIP_PASSWORD > noip_output.log 2>&1 &
 
echo "The scripts are now running in the background. You can close the terminal."

Now I can simply pull down from the Repo and rerun the script to update the website. In the future I would like to automate this using Github actions, that way, I dont have to ssh into my Pi everytime I push updates.

Update

I came around to setting up GithHub Workflow to automate my deployment. Before proceeding to setup the workflow, I knew I had to setup SSH to accept remote connections. I did the following to set everything up

  • I added Port Forwarding on Port 22 which is commonly used to for SSH connections.
  • Disabled Password Authentication to secure my Pi.
  • Added public key of my Host Machine to the Authorized List. Now that SSH is working, I can attempt to set up my Workflow. I pretty much relied on ChatGPT on this step as I have no knowledge on how GitHub workflows work. Here was the template code generated by GPT:
name: Deploy to Raspberry Pi
 
on:
  push:
    branches:
      - main  # Trigger on push to main branch
 
jobs:
  deploy:
    runs-on: ubuntu-latest
 
    steps:
      - name: Checkout code
        uses: actions/checkout@v2
 
      - name: Copy SSH key
        uses: shimataro/ssh-key-action@v2
        with:
          key: ${{ secrets.SSH_PRIVATE_KEY }}  # Add your SSH private key as a GitHub secret
          known_hosts: ${{ secrets.SSH_KNOWN_HOSTS }}  # Add the Raspberry Pi host info to known_hosts
 
      - name: Deploy to Raspberry Pi
        run: |
          ssh pi@<raspberry-pi-ip-address> 'bash -s' < ./deploy-script.sh
        env:
          SSH_AUTH_SOCK: /tmp/ssh-agent.sock

I ran into a pretty annoying issue that took me sometime to figure out, although, in hindsight it was somewhat obvious as it usually goes. The ssh step grabbed my script file and executed on the Pi.

All is good, however, when it executed through the GitHub "WorkFlow environment", there was a Node version mismatch. I was baffled and I made sure that the script ran locally by SSH'ing to my pi and running it. It was working perfectly fine and there were Node version mismatch did not occur. After a few attempts at trying different things, I came to the realization that there were different Node.js paths. I verified this by running

which node -a

which listed 3 different paths.

I finally understood why this was happening. My current solution was to then specify the Node path manually in the deploy script and it seemed to work. This was the specific bash command I added to my deploy script.

export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"

Now that the script file is fixed, I made a few modifications to secure my deployment. My final yaml file looks like this:

name: Deploy to Raspberry Pi
 
on:
  push:
    branches:
      - main
 
jobs:
  deploy:
    runs-on: ubuntu-latest
 
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
 
      - name: Set up SSH key
        uses: shimataro/ssh-key-action@v2
        with:
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          known_hosts: ${{ secrets.SSH_KNOWN_HOSTS }}
 
      - name: Deploy to Raspberry Pi
        run: |
          ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} "./${{ secrets.DEPLOY_SCRIPT }}"

and everything seems to be working ok. In the future, I will probably try to figure out the Node.js version mismatch issue and try to implement an alternative solution, one that is not preferably not hard coded.