What GitHub Actions is and why you don’t need a separate CI server anymore
Previously, to automate code testing, you had to:
- set up a separate server with Jenkins / Travis CI / GitLab CI;
- configure webhooks from GitHub to your server;
- maintain updates, security, and availability of that server;
- pay for infrastructure, even if you only need to run tests once a day.
GitHub Actions solves this differently: you just add a YAML file to your repository — and GitHub runs tests, builds, and deploys on its own servers. Nothing to install, nothing to administer.
Simply put: you describe what needs to be done. GitHub does it for you.
The problem / context: why lack of automation is a risk
Without CI (Continuous Integration), everything works manually: someone makes a commit, someone else locally runs tests, and it ends with “seems to work.” But when:
- commits happen frequently (several per day);
- the team has multiple people;
- there are dependencies on third-party services (database, API);
- deployment needs to be reliable;
the manual process becomes a bottleneck. Someone forgets to run tests, someone skips checking dependencies, and broken code goes to production at the worst possible moment.
GitHub Actions ensures every commit is automatically tested. It’s not up to a person to decide whether to test — it happens every time.
How it works in simple terms
GitHub Actions follows this model:
1. Workflow — describing what needs to happen
This is a YAML file in the .github/workflows/ folder of your repository. For example, .github/workflows/test.yml:
name: Run tests
on:
push:
branches: [main, master]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm ci
- run: npm test
Breaking it down:
name— the workflow name (visible in the GitHub interface).on— when to run (push tomainormaster).jobs— list of tasks. Here we have one task:test.runs-on— which server to run on (latest Ubuntu version).steps— the sequence: checkout code, install Node.js, install dependencies, run tests.
2. GitHub detects changes and starts running
When someone pushes code to main, GitHub finds the YAML file, reads it, and triggers the workflow. On the Actions tab in your repository, you’ll see the status: green ✓ (success) or red ✗ (failure).
3. The runner executes each step
GitHub provisions a virtual server (runner), clones your repository, runs each step in sequence, and reports success or failure.
4. Dependencies between jobs
If you need to build before testing:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm run build
test:
needs: build # test waits for build to finish
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm test
Most common scenarios
1. Automated code testing
The simplest and most useful starting point. Every commit — automatic tests. If tests fail, you find out immediately, not a month later.
2. Deploy to production after tests
deploy:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- run: npm ci && npm run build
- run: ./deploy.sh
env:
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}
Secrets (secrets.DEPLOY_TOKEN) are stored in repository settings and never end up in your code.
3. Deploy to staging on pull request
deploy-staging:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci && npm run build
- run: ./deploy-staging.sh
This way you can preview changes on a test server before merging into the main branch.
4. Scheduled tasks — cron
nightly:
runs-on: ubuntu-latest
on:
schedule:
- cron: '0 2 * * *' # daily at 02:00 UTC
steps:
- run: ./nightly-check.sh
Common mistakes
1. Hardcoding secrets in the workflow
Tokens, passwords, and keys should never be in YAML. Use secrets. — they are stored in repository settings and don’t appear in commit history.
2. Ignoring action versions
actions/checkout@v4 is a specific version. Don’t write actions/checkout@main or @latest — things can change and break your workflow. Pin your versions.
3. Running everything on one runner
If a workflow does the build, tests, and deploy all on one server — a failure in one step ruins everything. Split into separate jobs.
4. Not checking whether the workflow works at all
Created the file — make sure GitHub actually picked it up. The Actions tab should show a run. If it’s empty, check the file path and YAML syntax.
5. Using outdated images
ubuntu-latest updates over time, but specific versions like ubuntu-20.04 can become unavailable. Always check supported images on the GitHub docs.
Conclusion / action plan
GitHub Actions is the simplest way to add CI/CD to your project without a separate server. A single file in your repository — and everything works.
What to do next:
- Create
.github/workflows/in your repository. - Add your first workflow with tests and push to any branch.
- Check the Actions tab — did the workflow start and pass?
- Add staging deployment when a pull request is created.
- Save secrets in Settings → Secrets — never hardcode them in YAML.
Automation isn’t a “when we grow up” thing. It’s a “from day one” thing, and GitHub Actions makes it practically free.