Skip to content

How to Create Your First GitHub Actions Workflow: A Beginner's Guide

When I first tried automating my repository tasks, I kept doing everything manually - running tests, labeling issues, deploying changes. Then I discovered GitHub Actions, and I realized I could automate all of this with a single YAML file.

I struggled at first with YAML syntax errors and permission issues. After several failed attempts, I finally understood the structure. Here’s what I learned about creating your first GitHub Actions workflow.

Understanding the Workflow Structure

GitHub Actions workflows use YAML syntax with three main sections:

  • name: A descriptive title for your workflow
  • on: Triggers that start the workflow
  • jobs: The actual work to perform

GitHub provides hosted runners (Ubuntu, Windows, macOS) at no cost for public repositories. You don’t need to set up any servers.

The uses keyword pulls prebuilt actions from GitHub Marketplace, while run executes shell commands directly. GitHub also automatically creates a GITHUB_TOKEN for authentication in your workflows.

Creating Your First Workflow

Step 1: Create the Workflow Directory

In your repository, create the .github/workflows/ directory. This exact path is required - placing files anywhere else won’t work.

Step 2: Choose a Descriptive Filename

Use clear, purpose-driven names like build-and-test.yml or label-new-issue.yml. Avoid generic names like workflow.yml or ci.yml.

Step 3: Define Your Workflow

Here’s a complete example that automatically labels new issues:

.github/workflows/label-issues.yml
name: Label New Issues
on:
issues:
types: [opened]
jobs:
label-issues:
runs-on: ubuntu-latest
permissions:
issues: write
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Add triage label
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ISSUE_NUMBER: ${{ github.event.issue.number }}
LABEL: "triage"
run: gh issue edit "$ISSUE_NUMBER" --add-label "$LABEL"

Let me break down each part:

name: Label New Issues

This appears in the Actions tab of your repository, making it easy to identify.

Trigger definition
on:
issues:
types: [opened]

The on section defines when this workflow runs. Here, it triggers when a new issue is opened.

Job definition
jobs:
label-issues:
runs-on: ubuntu-latest

Each job needs a runner. ubuntu-latest is the most common choice and works for most workflows.

Permissions
permissions:
issues: write
contents: read

Permissions control what your workflow can access. Without issues: write, the workflow would fail when trying to add labels.

Steps
steps:
- name: Checkout repository
uses: actions/checkout@v6

The uses keyword references an action from GitHub Marketplace. The checkout action is essential - it clones your repository so subsequent steps can access your code.

Running commands
- name: Add triage label
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ISSUE_NUMBER: ${{ github.event.issue.number }}
LABEL: "triage"
run: gh issue edit "$ISSUE_NUMBER" --add-label "$LABEL"

The run keyword executes shell commands. Environment variables are set via env, and GitHub’s context values like github.event.issue.number are accessed with ${{ }} syntax.

Common Trigger Examples

Workflows can trigger on various events:

Push to main branch
on:
push:
branches: [main]
Pull request events
on:
pull_request:
types: [opened, synchronize]
Scheduled execution
on:
schedule:
- cron: '0 9 * * *'

The scheduled trigger uses cron syntax - 0 9 * * * runs daily at 9 AM UTC.

Common Mistakes to Avoid

I made these mistakes when starting out:

Wrong directory location: Placing workflow files directly in .github/ instead of .github/workflows/. The workflow simply won’t be detected.

YAML indentation errors: YAML requires consistent indentation. I once mixed tabs and spaces, causing syntax errors that were hard to debug.

Missing permissions: My workflows failed silently because I forgot to add issues: write for labeling operations. Always check what permissions your workflow needs.

Testing before merging: Workflows only run from the main branch (or default branch) by default. I spent hours debugging a workflow that was never triggered because I hadn’t merged it to main.

Using non-existent labels: When I referenced a label that didn’t exist in my repository, the workflow failed. Make sure labels are created before referencing them.

Testing Your Workflow

After creating your workflow file:

  1. Commit and push to your repository
  2. Merge to your main branch (if using a feature branch)
  3. Trigger the workflow by opening an issue or pushing code (depending on your on configuration)
  4. Navigate to the Actions tab in your repository to view the run
  5. Click on the workflow run to see detailed logs

Next Steps

Once you’re comfortable with basic workflows, explore:

  • Matrix builds: Test across multiple Node.js versions or operating systems
  • Secrets management: Store API keys securely in repository settings
  • Reusable workflows: Create workflow templates for common patterns
  • Environment protection rules: Require approvals before deploying to production

The GitHub Actions Marketplace contains thousands of pre-built actions for common tasks - from deploying to AWS to sending Slack notifications. Before writing custom scripts, check if an action already exists.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments