DevOps

GitHub Actions Tutorial: Automating Your Deployment Workflow

Automation With Github Actions automation has completely transformed how I handle deployments. Back in the old days (which wasn’t actually that long ago), we relied on clunky webhooks on github and custom scripts to get our code from repository to production. While those methods worked, but they were absolutely painful to maintain.

In this Github Actions tutorial, I’m going to show you EXACTLY how to create powerful, flexible deployment workflows that will save you countless hours of manual work. Trust me, once you start automating your deployments, you’ll never want to go back to the old ways.

What Are GitHub Actions?

GitHub Actions is a CI/CD (Continuous Integration/Continuous Deployment) platform built directly into GitHub. It lets you automate your build, test, and deployment pipeline without needing to set up any external systems.

Unlike the old webhook approach that required maintaining separate build scripts and servers, GitHub Actions handles everything within the GitHub ecosystem. This means faster setup, better integration, and less headache.

The best part? Every GitHub repository comes with free build minutes each month – 2,000 minutes for public repositories and 3,000 minutes for private repositories on the free plan. That’s seriously generous, especially for smaller projects.

Tip 💡: level up your productivity with our git commands cheat sheet!

Why You Need GitHub Actions

Before diving into the how-to, let’s talk about why you absolutely need to be using GitHub Actions:

  1. Save tremendous time by automating repetitive deployment tasks
  2. Reduce human errors that inevitably happen during manual deployments
  3. Maintain consistent environments across development, staging, and production
  4. Get instant feedback when something breaks
  5. Create robust testing workflows that run automatically
  6. Scale easily as your project grows

Honestly, the benefits are so substantial that I can’t imagine working without automated workflows anymore.

Getting Started with GitHub Actions

Setting up your first GitHub Action workflow is incredibly straightforward. Let’s break it down step by step:

Step 1: Create Your Workflow File

GitHub Actions workflows are defined in YAML files stored in the .github/workflows directory of your repository. Let’s create a simple deployment workflow:

  1. In your repository, create the directory .github/workflows if it doesn’t already exist
  2. Create a new file called deploy.yml in that directory

Here’s a basic workflow to get you started:

name: Deploy Application

on:
  push:
    branches: [ main ]
  workflow_dispatch:  # Allows manual triggering

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v3
      
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '16'
      
      - name: Install dependencies
        run: npm install
      
      - name: Build application
        run: npm run build
      
      - name: Deploy to server
        uses: easingthemes/ssh-deploy@v4.1.8
        with:
          SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
          REMOTE_HOST: ${{ secrets.REMOTE_HOST }}
          REMOTE_USER: ${{ secrets.REMOTE_USER }}
          SOURCE: "dist/"
          TARGET: "/var/www/html/"Code language: PHP (php)

This workflow will trigger whenever someone pushes to the main branch, checkout the code, set up Node.js, install dependencies, build the application, and then deploy it to your server using SSH.

Step 2: Set Up Secrets

For the deployment step, we need to securely store our SSH credentials. GitHub provides a secure way to store sensitive information using Secrets:

  1. Go to your repository settings
  2. Click on “Secrets and variables” and then “Actions”
  3. Click “New repository secret”
  4. Add secrets for SSH_PRIVATE_KEY, REMOTE_HOST, and REMOTE_USER

These secrets are encrypted and only exposed to the GitHub Actions workflow during run time. They’ll never be visible in logs or to people who don’t have admin access to your repository.

Step 3: Push Your Changes

Commit your workflow file and push it to your repository. This will automatically trigger the workflow if you’re pushing to the main branch.

Understanding Workflow Syntax

Let’s break down the key components of a GitHub Actions workflow:

Triggers

The on section defines when your workflow should run:

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]
  schedule:
    - cron: '0 0 * * *'  # Run daily at midnight
  workflow_dispatch:     # Manual triggerCode language: PHP (php)

You can trigger workflows on pushes, pull requests, on a schedule using cron syntax, or manually using the workflow_dispatch event.

Jobs

Workflows are made up of one or more jobs that can run in parallel or sequentially:

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      # Test steps here
  
  build:
    needs: test  # This job will only run after 'test' completes
    runs-on: windows-latest
    steps:
      # Build steps hereCode language: PHP (php)

The needs keyword creates dependencies between jobs, ensuring they run in the right order.

Runners

The runs-on property specifies the type of machine to run the job on. GitHub provides runners for Linux, Windows, and macOS:

  • ubuntu-latest (Linux)
  • windows-latest (Windows)
  • macos-latest (macOS)

You can also set up self-hosted runners if you need specialized environments.

Steps

Each job consists of a sequence of steps:

steps:
  - name: Checkout code
    uses: actions/checkout@v3
  
  - name: Run a command
    run: echo "Hello, World!"
  
  - name: Run a script
    run: |
      echo "This is a multi-line script"
      ./my-script.sh
      echo "Script completed"Code language: PHP (php)

Steps can either use pre-built actions (with the uses keyword) or run commands directly (with the run keyword).

Advanced GitHub Actions Features

Once you’re comfortable with the basics, you can leverage these advanced features:

Environment Variables

You can define environment variables at the workflow, job, or step level:

env:
  GLOBAL_VAR: "Available to all jobs"

jobs:
  build:
    env:
      JOB_VAR: "Available to all steps in this job"
    steps:
      - name: Step with env vars
        env:
          STEP_VAR: "Available only in this step"
        run: echo "$GLOBAL_VAR $JOB_VAR $STEP_VAR"Code language: JavaScript (javascript)

Matrix Builds

Matrix builds let you test across multiple configurations simultaneously:

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [12.x, 14.x, 16.x]
        os: [ubuntu-latest, windows-latest]
    steps:
      - uses: actions/checkout@v3
      - name: Use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v3
        with:
          node-version: ${{ matrix.node-version }}
      - run: npm testCode language: PHP (php)

This would run your tests against Node.js 12, 14, and 16 on both Ubuntu and Windows – a total of 6 different combinations.

Caching Dependencies

To speed up your workflows, cache dependencies between runs:

steps:
  - uses: actions/checkout@v3
  
  - name: Cache npm dependencies
    uses: actions/cache@v3
    with:
      path: ~/.npm
      key: npm-${{ hashFiles('package-lock.json') }}
      restore-keys: npm-
  
  - name: Install dependencies
    run: npm installCode language: PHP (php)

This can dramatically reduce build times for subsequent runs.

Implementing Multi-Environment Deployments

One of the most powerful features of GitHub Actions is the ability to deploy to different environments based on branch or event.

Here’s how to set up a workflow for deploying to both staging and production:

name: Deploy Pipeline

on:
  push:
    branches: [develop, main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Setup application
        run: |
          npm install
          npm run build
      
      - name: Deploy to Staging
        if: github.ref == 'refs/heads/develop'
        uses: easingthemes/ssh-deploy@v4.1.8
        with:
          SSH_PRIVATE_KEY: ${{ secrets.STAGING_SSH_KEY }}
          REMOTE_HOST: ${{ secrets.STAGING_HOST }}
          REMOTE_USER: ${{ secrets.STAGING_USER }}
          SOURCE: "dist/"
          TARGET: "/var/www/staging/"
      
      - name: Deploy to Production
        if: github.ref == 'refs/heads/main'
        uses: easingthemes/ssh-deploy@v4.1.8
        with:
          SSH_PRIVATE_KEY: ${{ secrets.PRODUCTION_SSH_KEY }}
          REMOTE_HOST: ${{ secrets.PRODUCTION_HOST }}
          REMOTE_USER: ${{ secrets.PRODUCTION_USER }}
          SOURCE: "dist/"
          TARGET: "/var/www/production/"Code language: JavaScript (javascript)

In this workflow, pushing to the develop branch deploys to staging, while pushing to main deploys to production. The if conditions ensure each deployment step only runs for the appropriate branch.

Testing Before Deployment

A robust CI/CD pipeline should include testing to prevent deploying broken code. Here’s how to add testing to your workflow:

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Set up Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '16'
      
      - name: Install dependencies
        run: npm install
      
      - name: Run tests
        run: npm test
  
  deploy:
    needs: test  # Only deploy if tests pass
    runs-on: ubuntu-latest
    steps:
      # Deployment steps hereCode language: PHP (php)

By adding the needs: test line, the deployment job will only run if the test job completes successfully.

Troubleshooting Common Issues

Even with automation, things sometimes go wrong. Here are some common issues and how to fix them:

Failed Deployments

If your deployment fails, first check the workflow logs for error messages. Common problems include:

  • Permission issues: Ensure your SSH user has the necessary permissions
  • Connection problems: Verify host address and network configurations
  • Space limitations: Check if your server has enough disk space

Debugging Workflows

For complex debugging, you can set up SSH access to the runner:

steps:
  - name: Setup tmate session
    if: ${{ failure() }}
    uses: mxschmitt/action-tmate@v3

This creates an SSH session to the runner if a previous step fails, allowing you to investigate interactively.

Best Practices for GitHub Actions

To make the most out of GitHub Actions, follow these best practices:

  1. Keep workflows focused: Create separate workflows for different purposes (testing, deployment, etc.)
  2. Use official actions: Prefer well-maintained actions from verified creators
  3. Set timeout limits: Add timeout-minutes to prevent stuck jobs from running forever
  4. Add status badges: Include workflow status badges in your README
  5. Use concurrency limits: Prevent parallel runs of the same workflow with concurrency settings
  6. Add manual approval for sensitive environments using environment configurations

Conclusion

GitHub Actions has completely revolutionized how we approach deployment automation. With its tight integration into GitHub, extensive marketplace of pre-built actions, and powerful workflow capabilities, it’s now easier than ever to create robust CI/CD pipelines.

I’ve personally migrated all my projects from the old webhook-based approach to GitHub Actions, and the difference is night and day. Deployments are now consistent, reliable, and completely hands-off.

If you’re still using manual deployments or older automation tools, I strongly encourage you to give GitHub Actions a try. The time savings alone make it worth the initial setup investment, not to mention the peace of mind that comes from knowing your deployments are handled automatically.

Have you implemented GitHub Actions in your workflow? Let me know about your experiences in the comments below!

Additional Resources

Rana Ahsan

Rana Ahsan is a seasoned software engineer and technology leader specialized in distributed systems and software architecture. With a Master’s in Software Engineering from Concordia University, his experience spans leading scalable architecture at Coursera and TopHat, contributing to open-source projects. This blog, CodeSamplez.com, showcases his passion for sharing practical insights on programming and distributed systems concepts and help educate others. Github | X | LinkedIn

Recent Posts

Automation With Python: A Complete Guide

Tired of repetitive tasks eating up your time? Python can help you automate the boring stuff — from organizing files to scraping websites and sending…

2 weeks ago

Python File Handling: A Beginner’s Complete Guide

Learn python file handling from scratch! This comprehensive guide walks you through reading, writing, and managing files in Python with real-world examples, troubleshooting tips, and…

4 weeks ago

Service Worker Best Practices: Security & Debugging Guide

You've conquered the service worker lifecycle, mastered caching strategies, and explored advanced features. Now it's time to lock down your implementation with battle-tested service worker…

1 month ago

This website uses cookies.