Automating Deployments with GitHub Actions

November 29, 2024 (1mo ago)8 min read

Automating Deployments with GitHub Actions

Deploying a Next.js app is a thrilling milestone. But manual deployments? They can quickly turn excitement into frustration. It's not just the repetition—it's the risk of missing a step or making a mistake that could bring your app crashing down. What if you could push a button—or better yet, let GitHub handle it for you?

That's where GitHub Actions comes in. By automating your deployments, you can focus on building and improving your app instead of worrying about repetitive tasks. Think of it as setting up an autopilot for your development workflow.

In this post, I'll guide you through using GitHub Actions to automate deployments for your Next.js app. Let's make your workflow smoother and more reliable!


Why Automate Deployments?

Manual deployments are not only tedious—they're prone to errors. All it takes is skipping a step or running the wrong command to end up with downtime or a broken build, or even data loss. It's stressful, and let's face it: nobody wants that on a Friday night.

Automating your deployments eliminates these risks, ensuring consistency, reducing human error, and freeing up your time for tasks that matter more.

Automating deployments solves these problems:

  • 🚀 Speed: Push your changes, and GitHub Actions will take care of the rest.
  • Reliability: Every step happens in the correct order, every time.
  • 📈 Scalability: As your project grows, automation keeps things running smoothly.
  • 😌 Peace of Mind: No more late-night deployment disasters—just clean, consistent workflows.

Step 1: Preparing Your Environment

Before diving into GitHub Actions, let's prepare the groundwork for success. Think of this step as setting the stage for a smooth, stress-free automation process. With a bit of preparation now, you'll save countless hours (and avoid headaches) down the line.

  1. A Deployed App Ensure your app is already live on a platform like Vercel, DigitalOcean, etc. For this guide, I'll assume you're using DigitalOcean.

  2. A GitHub Repository Your app's source code should be hosted on GitHub. If you haven't already, create a new repository and push your code to it.

  3. Access Credentials Gather the credentials you'll need to deploy, such as an SSH key or an API token. For DigitalOcean, you'll need:

    • A personal access token (PAT) with write access to your account.
    • Your droplet IP address and SSH key.

Step 2: Setting Up a GitHub Actions Workflow

Now that your environment is set, let's move to the exciting part—building a workflow that takes care of your deployments, so you don't have to. Think of this as a blueprint where you specify what needs to happen, when it should happen, and how GitHub should handle it—all in one YAML file.

2.1 Understanding the Workflow File

A GitHub Actions workflow file is written in YAML format. It defines:

  • Name: A descriptive label for your workflow.
  • Trigger Events: Specify when the workflow should run (e.g., on push, on pull_request, or manually).
  • Jobs: Groups of tasks that execute in a virtual environment, such as building or deploying your app.

This file will serve as your automation script, making deployments predictable and error-free.

2.2 Create a Workflow File

  1. Navigate to the Workflow Directory
    Ensure your repository contains a .github/workflows/ directory. If it doesn't exist, create it at the root level of your project.

  2. Create the Workflow File
    Inside the directory, create a file called deploy.yml. This file will define the workflow for automating your deployment.

2.3 Define the Workflow

Now, let's break the workflow file into its main sections:

Workflow Name

The name block is a simple but important detail—it identifies your workflow in the GitHub Actions UI.

yaml
1name: Deploy to DigitalOcean

Trigger Events

The on block defines when the workflow should run. In this case:

  • push: Runs the workflow automatically whenever changes are pushed to the main branch, but only if the modified files are within specified directories.
  • workflow_dispatch: Allows you to trigger the workflow manually via the GitHub interface.
yaml
1on:
2  push:
3    branches: ["main"]
4    paths:
5      - "src/**"
6      - "content/**"
7      - "public/**"
8  workflow_dispatch:

The on block specifies when your workflow should run. In this example:

  • push: Runs automatically whenever you push changes to the main branch, but only if the modified files are in src/, content/, or public/.

    The paths directive ensures that the workflow runs only when changes are made to specific directories, avoiding unnecessary triggers.

  • workflow_dispatch: Lets you trigger the workflow manually from the GitHub Actions interface.

Jobs Section

The jobs block is where the magic happens—it organizes your tasks into "jobs," which GitHub can execute independently.
Here, we're defining a single job called build that runs on an Ubuntu virtual machine.

yaml
1jobs:
2  build:
3    runs-on: ubuntu-latest

2.4 Adding Deployment Steps

Each job consists of steps, which are the specific tasks GitHub will execute in order. Let's break the deployment process into logical steps and see how to configure each one:

What Does This Step Do?

Deploying your app to a DigitalOcean droplet involves:

  1. Connecting to the server via SSH.
  2. Pulling the latest changes from your Git repository.
  3. Installing dependencies and building the app.
  4. Restarting the app using a process manager like PM2.

How Do You Configure It?

Here's the YAML code to define these steps in your workflow file:

yaml
1steps:
2  - name: Deploy to DigitalOcean droplet via SSH
3    uses: appleboy/ssh-action@v1.2.0
4    with:
5      host: ${{ secrets.SERVER_HOST }}
6      username: ${{ secrets.SERVER_USERNAME }}
7      key: ${{ secrets.SERVER_PRIVATE_KEY }}
8      port: ${{ secrets.SERVER_PORT }}
9      script: |
10        #!/bin/bash
11        # Ensure Node.js and npm are in the PATH
12        export PATH=$PATH:/home/ubuntu/.nvm/versions/node/v22.11.0/bin
13
14        # Navigate to the app directory and pull the latest changes
15        cd <PATH_TO_YOUR_APP>
16        git pull
17        npm install
18        npm run build
19
20        # Check if PM2 process is running; restart or start accordingly
21        if pm2 list | grep -q "<APP_NAME>"; then
22          pm2 reload <APP_NAME>
23        else
24          pm2 start npm --name "<APP_NAME>" -- start
25        fi
26
27        # Verify the running processes
28        pm2 list

Breaking It Down:

  • uses: Indicates the GitHub Action being utilized. In this case, appleboy/ssh-action allows executing commands over SSH.

  • with: Contains the settings and secrets needed for the deployment, including:

    • host: Your server's IP address (stored as a GitHub Secret).
    • username: The server username, such as root or ubuntu.
    • key: Your private SSH key for secure authentication.
    • port: The port for SSH (default is 22).
  • script: Contains the series of commands executed on your server. These include:

    • Adding Node.js to the PATH: Ensures the build tools can run properly.
    • Pulling the latest code: Syncs your server with the latest GitHub changes.
    • Installing dependencies and building the app: Prepares your app for deployment.
    • Managing the app with PM2: Either restarts or starts the app, ensuring minimal downtime.

Step 3: Setting Up GitHub Secrets

GitHub Actions requires sensitive information like credentials to be stored securely as secrets. These secrets are encrypted and can be accessed by your workflows.

  1. Go to your repository's Settings > Secrets and variables > Actions.
  2. Add the following secrets:
    • SERVER_HOST: Your droplet's IP address.
    • SERVER_USERNAME: Your droplet's username (e.g., root).
    • SERVER_PRIVATE_KEY: Your SSH private key.
    • SERVER_PORT: Your SSH port (default is 22).

With these secrets in place, your workflow can securely access your server—without the risk of hardcoding sensitive credentials in the YAML file.


Step 4: Testing Your Workflow

Now it's time for the moment of truth: testing your workflow. This step is both exciting and nerve-wracking—like starting up a freshly built machine for the first time. Here's how to make sure everything is running as expected.

  1. Push a change to the main branch (e.g., update the README file).
  2. Go to the Actions tab in your repository to monitor the workflow's progress.
  3. Check your server to verify the app has been deployed successfully.

If something goes wrong, don't panic. GitHub Actions provides detailed logs to help you debug issues step by step. You'll be able to see exactly where things went wrong and make the necessary adjustments.


What's Next?

Setting up GitHub Actions is just the first step in optimizing your workflow. From adding pre-deployment tests to fine-tuning workflows for specific environments, the possibilities are endless. As you dive deeper into what's possible, you'll find endless opportunities to refine your process and focus on what really matters: building great apps.

Adding Pre-Deployment Tests

Ensure your code is deployment-ready by running automated tests before every push to production.

Optimizing for Multiple Environments

Learn how to manage workflows for staging, production, and more.

Proactive Monitoring

Keep tabs on your deployments by integrating tools that provide real-time alerts and analytics.

Stay tuned for these and other insights to make your development process faster, more reliable, and stress-free.


Automating deployments with GitHub Actions isn't just about saving time—it's about building confidence in your workflow. By eliminating manual steps, you reduce errors and focus on what you do best: creating amazing apps.

I hope this guide helps you take the next step in streamlining your deployment process. Have questions or insights? Let me know—I'd love to hear from you!


Remember that you can find me as J1Loop on github. Or you can contact me on LinkedIn.