Young Devs Bin
๐Ÿ”ง Dev Log

Setting Up Auto Deploy with GitHub Actions

ยท6 min readยท#ci-cd#github-actions#nextjs#deployment

Why Did I Need This?

My blog runs on Next.js. Before you can serve it, you need to build it first. If you're an Android developer like me, think of it like compiling your Kotlin code into an APK โ€” you can't just throw raw source code at a device and expect it to run. Next.js works the same way: your code has to be transformed into something the server can actually serve.

The problem was my shared hosting server didn't have enough memory to run the build process. Every time I tried, it just crashed. So I had no choice but to do everything manually on my local machine:

  1. Build the project locally
  2. Compress the build output into a zip file
  3. Transfer it to the server
  4. SSH into the server
  5. Extract the files
  6. Restart the app

Every. Single. Time I made a change.

Honestly it was really annoying. So I started looking for a way to automate this โ€” and that's where GitHub Actions came in.

โŒ Before: Update code โ†’ Build locally โ†’ Compress โ†’ Transfer โ†’ Extract โ†’ Restart (manually, every single time)

โœ… After: Update code โ†’ git push โ†’ GitHub handles everything automatically

I'm still learning a lot of this stuff, so setting this up took some time โ€” but it was a really good learning experience. Let me walk through what I did.


What is GitHub Actions?

GitHub has a feature called Actions. When I push code, GitHub automatically runs a list of tasks I defined.

It feels like this:

Me: "Hey, I pushed some code!" GitHub: "Got it! I'll build it and send it to your server." Me: "Thanks ๐Ÿ‘"

That "task list" lives in a file inside .github/workflows/. If you want to dive deeper, the official GitHub Actions documentation is actually pretty good.


Breaking Down the Workflow File

name: your-workflow-name   # name it whatever you want
 
on:
  push:
    branches:
      - main                 # Only runs when I push to main branch

This controls when it runs. Every time I git push, it kicks off automatically.

runs-on: ubuntu-latest

GitHub fires up a temporary Linux computer on their servers. That's where the build happens โ€” not on my weak shared hosting.

- name: Install dependencies
  run: npm ci
 
- name: Build
  run: npm run build

Install packages, then build Next.js. Same thing I'd do on my laptop locally โ€” but GitHub's computer does it for me.


Now that GitHub can build the app, the next step is getting those built files onto my server. But to do that, GitHub needs a way to securely connect to my server โ€” and that's where SSH keys come in.

What's an SSH Key?

To send files to my server, I need to prove "I'm the actual owner." Instead of a password, I use an SSH key.

Think of it like a lock and key:

๐Ÿ”‘ Private Key = The key in my hand โ†’ Never share this with anyone

๐Ÿ”’ Public Key = The lock installed on the server โ†’ Has to match the key to get in

GitHub Actions needs this key to connect to my server.


What are GitHub Secrets?

I can't just paste my private key into the workflow file โ€” if the repo is public, anyone could grab it.

That's why GitHub Secrets exist. It's like a vault โ€” I store the value there and just reference it by name:

ssh-private-key: ${{ secrets.YOUR_SECRET_NAME }}

The actual value is stored in GitHub Settings > Secrets, and it shows up as *** in logs.

I stored three secrets in total โ€” one for the private key, one for the server address, and one for the server username. These get referenced in the workflow file and never appear in plain text anywhere.


The Struggles (Real Talk)

Problem 1: SSH Key Format Error

I kept getting a cryptographic error when trying to load the key.

Why: SSH keys have lots of line breaks. When you pull them from GitHub Secrets, those line breaks can get corrupted, which breaks the key entirely.

What I tried:

  1. Writing the key directly to a file โ†’ failed
  2. Different formatting approaches โ†’ failed
  3. Using a dedicated SSH agent action โ†’ worked โœ…

A dedicated SSH agent action is specifically built to handle SSH key formatting issues, so it takes care of everything automatically.


Problem 2: File Transfer Tool Not Available

My first choice for file transfer wasn't available on shared hosting.

Fix: Switched to a combination of compression + secure file transfer that's available on most servers.

The idea:

  1. Compress the build output
  2. Transfer the compressed file securely
  3. Extract on the server
  4. Clean up

Problem 3: Non-standard SSH Port

My hosting provider uses a non-standard SSH port instead of the default 22.

Important note: the two SSH-based tools I was using have different flags for specifying the port โ€” easy to mix up and waste a lot of time on.


Problem 4: Node.js Version Warning

Node.js 20 actions are deprecated

GitHub Actions is updating their internal Node.js version. Fixed with one environment variable in the workflow file.


Final Flow

1. git push โ†’ Code goes to GitHub

2. GitHub Actions kicks off
   โ”œโ”€โ”€ Spins up a temporary Linux machine
   โ”œโ”€โ”€ Downloads the code
   โ”œโ”€โ”€ Installs dependencies
   โ””โ”€โ”€ Builds the app

3. SSH authentication
   โ””โ”€โ”€ Handled securely via SSH agent

4. Build output transferred to server

5. Server restarts and serves the new version

Quick Summary

RoleWho Does It
Store codeGitHub
BuildGitHub Actions (free computer)
Transfer filesSecure file transfer over SSH
Run web serverShared hosting provider

Now all I do is write a post and git push. That's it.