Getting Started with GitHub Actions
GitHub Actions is a built-in automation tool that lives right inside every GitHub repository. It lets you run commands automatically whenever something happens in your repo, such as someone pushing code or opening a pull request. Because it is part of GitHub, you do not need to set up a separate server or sign up for another service to get started, which makes it the fastest on-ramp into CI/CD (Continuous Integration and Continuous Delivery, the practice of automatically building, testing, and shipping your code). In this page we will cover the core building blocks and write a minimal working pipeline.
What problem does it solve
Before automation, a developer had to remember to run the tests, build the project, and deploy it by hand every single time. People forget steps, run things on the wrong branch, or skip tests when they are in a hurry. GitHub Actions removes that human error by running a fixed set of steps the same way, every time, on a clean machine in the cloud.
When to use it: any time you have a repeatable task tied to a repo event — running tests on every push, building a Docker image, deploying to a server, or linting code in a pull request.
When NOT to use it: for very heavy, long-running jobs (think hours of GPU training) or workflows that need tight control over the underlying hardware, a dedicated CI tool like Jenkins on your own Ubuntu server may be cheaper and more flexible.
The core concepts
GitHub Actions is built from a small set of nested pieces. Learn these five terms and the rest falls into place.
| Term | What it means | Plain-English example |
|---|---|---|
| Workflow | A whole automated process, written in one YAML file. | ”Run my tests when code is pushed.” |
| Event (trigger) | The thing that starts a workflow. | A push, a pull_request, or a schedule. |
| Job | A group of steps that run together on one machine. | ”build”, “test”, “deploy”. |
| Step | A single task inside a job. | ”Check out the code” or “run npm test”. |
| Runner | The virtual machine that actually executes a job. | A fresh Ubuntu 24.04 VM that GitHub provides. |
YAML (short for “YAML Ain’t Markup Language”) is a simple text format that uses indentation to show structure. Workflows are always written in YAML, so spacing matters — use spaces, never tabs.
Where workflow files live
Every workflow file must sit in a special folder at the root of your repository: .github/workflows/. The leading dot means it is a hidden folder on Linux. GitHub automatically scans this folder and picks up any .yml or .yaml file inside it.
# from the root of your project on an Ubuntu machine
mkdir -p .github/workflows
ls -la .github/workflows
Output:
total 8
drwxrwxr-x 2 dev dev 4096 Jun 16 10:22 .
drwxrwxr-x 3 dev dev 4096 Jun 16 10:22 ..
The folder is empty for now. Next we add a workflow file to it.
A minimal workflow that runs on push
Let’s write the smallest useful workflow: it runs every time you push code, spins up an Ubuntu runner, checks out your repo, and prints a message. Create the file with any editor — here we use nano.
nano .github/workflows/ci.yml
Paste in the following:
name: CI
# This workflow runs whenever code is pushed to the repo
on:
push:
branches:
- main
jobs:
build:
# The runner: a clean Ubuntu virtual machine hosted by GitHub
runs-on: ubuntu-24.04
steps:
# Step 1: copy your repo's code onto the runner
- name: Check out the code
uses: actions/checkout@v4
# Step 2: run a simple command to prove it works
- name: Say hello
run: echo "Pipeline is running on $(uname -a)"
Let’s read this top to bottom:
name:is just a label shown in the GitHub UI.on: push: branches: [main]is the event — this workflow fires only on a push to themainbranch.jobs:holds one job calledbuild.runs-on: ubuntu-24.04tells GitHub to use an Ubuntu 24.04 LTS (Long-Term Support) runner.steps:lists the tasks in order.
Notice the two ways a step does work. uses: pulls in a pre-made action (reusable code published by others). Here actions/checkout@v4 is the official action that copies your repo onto the runner — almost every workflow starts with it. run: instead executes a raw shell command, exactly as you would type it in an Ubuntu terminal.
Gotcha: without the
actions/checkoutstep, the runner starts empty — your repository files are NOT there by default. Forgetting it is the single most common beginner mistake, and you will see “file not found” errors on commands that should obviously work.
Push it and watch it run
Commit and push the file. The push itself is the event that triggers the workflow.
git add .github/workflows/ci.yml
git commit -m "Add first GitHub Actions workflow"
git push origin main
Output:
[main 7f3a1c9] Add first GitHub Actions workflow
1 file changed, 18 insertions(+)
create mode 100644 .github/workflows/ci.yml
To github.com:yourname/yourrepo.git
a1b2c3d..7f3a1c9 main -> main
Now open your repository on GitHub and click the Actions tab. You will see a run named “CI” with a spinning yellow dot that turns into a green check when it succeeds. Click into it to read the live log of each step, including the output of your echo command.
Hosted runners vs self-hosted runners
GitHub gives you two kinds of runners. Knowing the difference saves money and headaches.
| Hosted runner | Self-hosted runner | |
|---|---|---|
| Who manages it | GitHub | You |
| Where it runs | GitHub’s cloud | Your own Ubuntu server |
| Setup effort | Zero | You install an agent via systemd |
| Cost | Free minutes, then per-minute billing | Your own hardware/cloud cost |
| Best for | Most projects, public repos | Special hardware, private networks, heavy use |
When to self-host: you need access to internal servers behind a firewall, special hardware, or you run so many minutes that GitHub’s billing gets expensive. Otherwise, stick with hosted runners — they are simpler and always patched.
Best practices
- Pin actions to a major version like
@v4, not a floating@main, so a third-party update cannot silently break your pipeline. - Keep workflows small and focused — one file per purpose (
ci.yml,deploy.yml) is far easier to read than one giant file. - Never hardcode passwords or tokens in a workflow file; use encrypted repository secrets instead (covered in the secrets page below).
- Always start a job with
actions/checkoutunless you have a deliberate reason not to. - Limit triggers with
branches:so you do not waste minutes running on every throwaway branch. - Read the run logs in the Actions tab when something fails — the failing step is highlighted in red with the exact command output.
- Use the latest LTS runner image (
ubuntu-24.04) so your CI matches a modern, supported Ubuntu environment.