Table of Contents
Introduction
GitHub Actions is an automation platform built into GitHub that lets you run workflows in response to events in your repositories. For DevOps on Linux, it is especially useful for automating builds, tests, deployments, and checks on code stored in GitHub.
This chapter focuses on how GitHub Actions works, how workflows are structured, and what is specific to using it in typical Linux based DevOps scenarios. General CI/CD principles and general YAML syntax are covered in other chapters, so here we will only connect those ideas to GitHub Actions itself.
Core Concepts of GitHub Actions
GitHub Actions revolves around a few core ideas: events, workflows, jobs, and steps. Everything you do in GitHub Actions is described in YAML files stored in your repository, usually under .github/workflows/.
A workflow is a configurable automation that runs when a specified event occurs. Typical events include a push to a branch, opening a pull request, or running a workflow manually. Within a workflow you define one or more jobs. Each job runs in its own virtual machine or container. Inside each job there are steps, which are individual actions, either shell commands or reusable "actions" provided by GitHub or the community.
Workflows are defined using YAML. For example, a very minimal workflow file might look like this:
name: Example workflow
on:
push:
branches: [ "main" ]
jobs:
example-job:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Run a command
run: echo "Hello from GitHub Actions on Linux"
This tells GitHub to run example-job on an Ubuntu Linux runner every time there is a push to the main branch.
Important rule: Every workflow file must be stored in the .github/workflows/ directory of the repository and must be valid YAML, otherwise GitHub Actions will ignore it.
Events and Triggers
GitHub Actions workflows start when an event occurs. The on section of a workflow file describes what should trigger the workflow. Some common triggers useful in DevOps are:
push events, which start workflows when commits are pushed, often to main or feature branches. pull_request events, which start workflows when pull requests are opened or updated. workflow_dispatch, which allows manual runs from the GitHub web interface. schedule, which uses cron style syntax to run workflows periodically.
For example, to run tests on every push and also allow manual triggering, you might write:
on:
push:
branches: [ "main", "develop" ]
workflow_dispatch:You can combine multiple events and even filter them, for example to react only when certain paths change. This flexibility lets you tune when your Linux build or deployment pipelines execute, without moving away from GitHub.
Jobs and Runners
Jobs are units of work that run in parallel by default. Each job runs on a runner. A runner is a machine with an agent that executes the workflow job. GitHub provides hosted runners, including several Linux images, or you can host your own runners on Linux machines.
In GitHub Actions, specifying a runner for a job is done with runs-on. For Linux based CI/CD, you will most often see:
runs-on: ubuntu-latest
This uses a GitHub hosted Ubuntu runner with a preinstalled set of tools. You can also specify specific versions such as ubuntu-22.04 if you need consistent behavior.
A job definition might look like this:
jobs:
build:
runs-on: ubuntu-latest
steps:
# steps here
If you have multiple jobs, they run in parallel unless you define dependencies. To make one job depend on another, use needs:
jobs:
build:
runs-on: ubuntu-latest
steps:
- run: echo "Building"
test:
runs-on: ubuntu-latest
needs: build
steps:
- run: echo "Testing after build"This can be useful for DevOps pipelines where you first build artifacts on a Linux runner, then test or deploy them in subsequent jobs.
Steps and Actions
Within a job, steps run sequentially in the same environment. Each step either uses an action or runs a command. Actions are reusable pieces of automation that encapsulate logic, while run steps are simple shell commands.
A typical sequence of steps in a Linux CI workflow is: check out the code, set up a language environment, install dependencies, run tests, and possibly build and upload artifacts.
For example:
steps:
- name: Check out repository
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Run tests
run: pytest
Each run step on Linux is executed using /bin/bash by default on the Ubuntu runners. This means you can rely on typical Linux command line behavior and tools in your workflow scripts.
Using the GitHub Hosted Linux Environment
When you use runs-on: ubuntu-latest, your jobs run inside a fresh virtual machine that runs a specific Ubuntu version with a large set of tools preinstalled. This environment is recreated for every job, so any files or changes you create do not persist between runs unless you explicitly store them as artifacts or cache them.
Common Linux command line tools are available, such as bash, git, curl, and package managers like apt. You can install additional tools in a step:
- name: Install system packages
run: |
sudo apt-get update
sudo apt-get install -y libpq-devSince each job starts from a clean Linux image, DevOps workflows on GitHub Actions tend to be predictable and reproducible. Any required tool or configuration should be described in the workflow or in scripts stored in the repository, not assumed to exist on the runner.
Caching and Artifacts in Linux Builds
To speed up repeated Linux builds or tests, GitHub Actions lets you cache files between workflow runs. This is especially important for package caches, compiled dependencies, or build outputs. Another related concept is artifacts, which are files that you want to store and download after a workflow run completes.
Caching is usually done with the official actions/cache action. You provide a key that represents a specific state of your dependencies. For example, to cache Python dependencies on Linux:
- name: Cache pip
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-This tells GitHub Actions to store the pip cache from a Linux runner and restore it on future runs when the requirements file has not changed.
Artifacts, in contrast, are meant as outputs you want to download. To upload build artifacts from a Linux build job:
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: linux-build
path: dist/
This preserves build outputs created in dist/ so they can be inspected or used by other jobs or people.
Environments, Secrets, and Security
In DevOps workflows, you often need to interact with external services, such as registries or cloud providers. To do this securely, GitHub Actions provides secrets and environments.
Secrets are encrypted values stored in the repository or organization settings. They are injected into workflows as environment variables. For example, you might define DOCKER_PASSWORD as a secret and then use it in a Linux job:
- name: Log in to registry
run: echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdinSecrets are never printed in logs in plain text. They are specific to GitHub Actions and are crucial for safe automation.
Environments allow you to group secrets and configure protection rules per environment, like staging or production. When deploying from a Linux runner to a production server, you can require manual approval or restrict who can trigger that deployment. In the workflow, you reference the environment:
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- run: ./deploy.shThis lets you structure your DevOps pipeline so that production deployments from Linux runners are controlled and auditable.
Important rule: Never hard code passwords, tokens, or API keys directly in workflow YAML or source code. Always store them as GitHub secrets and reference them with ${{ secrets.NAME }}.
Matrix Builds on Linux
Matrix builds are a GitHub Actions feature that lets you define multiple similar jobs across different parameters, such as versions, operating systems, or configurations, using a single job definition.
For Linux focused DevOps work, you might use a matrix to test multiple language versions, frameworks, or toolchains on the same Ubuntu runner. For example:
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [ "3.9", "3.10", "3.11" ]
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: pip install -r requirements.txt
- name: Run tests
run: pytestIn this case, your tests run three times on Linux, once for each Python version, without needing to write three separate jobs. Matrix builds help you keep Linux CI configurations concise and powerful.
Connecting GitHub Actions to External Linux Systems
GitHub Actions can act as a central automation hub while your actual runtime or production systems are separate Linux servers or cloud instances. A common pattern is to run build and test jobs on GitHub Actions’ Linux runners and then deploy from there to external Linux hosts.
To perform operations on remote Linux machines, you typically interact over SSH or use CLI tools. For example, you might use an action that wraps ssh, or simply call ssh directly if you have installed openssh-client and configured keys via secrets.
A simplified example that connects from the Ubuntu runner to a remote Linux server with SSH could be:
- name: Deploy to server
run: |
ssh -o StrictHostKeyChecking=no user@server.example.com "cd /var/www/app && ./deploy.sh"
env:
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}Before running this, you would import the private key into an SSH agent in an earlier step. This pattern lets you use GitHub Actions as a Linux based orchestrator that interacts with other Linux machines across your infrastructure.
Summary
GitHub Actions provides a flexible, GitHub integrated platform for automating DevOps workflows on Linux. Workflows are defined in YAML files, triggered by repository events, and run as jobs and steps on Linux runners such as ubuntu-latest. You can rely on a consistent Linux environment, use caching and artifacts to manage build data, protect sensitive information with secrets and environments, and coordinate with external Linux systems using SSH and other tools.
By understanding how events, jobs, runners, and steps work together, you can create robust CI/CD pipelines that run reliably within the GitHub ecosystem and operate across your wider Linux based infrastructure.