Skip to content
HostScout
dev-tools

GitHub Actions Guide: CI/CD for Beginners

Learn how to automate your development workflow with GitHub Actions. A practical guide to continuous integration and deployment for beginners.

H
HostScout Team
· · 8 min read
GitHub Actions Guide: CI/CD for Beginners

GitHub Actions transforms how developers build, test, and deploy code. Instead of manually running tests or deploying to servers, Actions automates these tasks every time you push code. This guide introduces GitHub Actions from scratch, showing you how to set up practical CI/CD workflows. ## What is GitHub Actions? GitHub Actions is a CI/CD (Continuous Integration/Continuous Deployment) platform built into GitHub. It lets you automate tasks triggered by repository events like pushes, pull requests, or schedules. Common uses:

  • Run tests automatically
  • Build and deploy applications
  • Lint code for style issues
  • Publish packages to npm/PyPI
  • Create releases
  • Send notifications ## Core Concepts ### Workflows A workflow is an automated process defined in a YAML file. Workflows live in .github/workflows/ in your repository. ### Events Events trigger workflows. Common triggers:
  • push - Code pushed to repository
  • pull_request - PR opened, updated, or merged
  • schedule - Cron-based timing
  • workflow_dispatch - Manual trigger ### Jobs Jobs are sets of steps that run on the same runner. Jobs run in parallel by default. ### Steps Steps are individual tasks within a job. Each step runs a command or action. ### Actions Actions are reusable units of code. Use existing actions or create your own. ### Runners Runners are servers that execute your workflows. GitHub provides hosted runners (Ubuntu, Windows, macOS) or you can self-host. ## Your First Workflow Let’s create a simple workflow that runs tests on every push. ### Step 1: Create Workflow File Create .github/workflows/test.yml: yaml name: Run Tests on: push: branches: [main] pull_request: branches: [main] jobs: test: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Node.js uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm' - name: Install dependencies run: npm ci - name: Run tests run: npm test ### Understanding the Workflow name: Display name for the workflow. on: Events that trigger the workflow. This runs on pushes and PRs to main. jobs: Defines jobs to run. We have one job called “test”. runs-on: Specifies the runner. ubuntu-latest is a GitHub-hosted Linux runner. steps: Sequential tasks within the job:
  1. Checkout code from repository
  2. Set up Node.js environment
  3. Install dependencies
  4. Run tests ## Practical Workflows ### Node.js CI/CD Complete workflow for a Node.js application: ```yaml name: Node.js CI/CD on: push: branches: [main, develop] pull_request: branches: [main] jobs: test: runs-on: ubuntu-latest strategy: matrix: node-version: [18, 20, 22] steps: - uses: actions/checkout@v4 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} cache: ‘npm’ - run: npm ci - run: npm run build —if-present - run: npm test deploy: needs: test runs-on: ubuntu-latest if: github.ref == ‘refs/heads/main’ steps: - uses: actions/checkout@v4 - name: Deploy to production run: | echo “Deploying to production…” # Add your deployment commands here
- **Matrix strategy**: Tests across multiple Node versions
- **Job dependencies**: `deploy` needs `test` to pass
- **Conditional execution**: Deploy only on main branch ### Python CI ```yaml
name: Python CI on: push: branches: [main] pull_request: branches: [main] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: '3.12' cache: 'pip' - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r requirements.txt pip install pytest pytest-cov - name: Run tests with coverage run: pytest --cov=src --cov-report=xml - name: Upload coverage uses: codecov/codecov-action@v4 with: file: ./coverage.xml
``` ### Static Site Deployment Deploy to GitHub Pages: ```yaml
name: Deploy to GitHub Pages on: push: branches: [main] permissions: contents: read pages: write id-token: write jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm' - name: Install and build run: | npm ci npm run build - name: Upload artifact uses: actions/upload-pages-artifact@v3 with: path: './dist' deploy: needs: build runs-on: ubuntu-latest environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} steps: - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v4
``` ### Docker Build and Push ```yaml
name: Docker Build on: push: branches: [main] tags: ['v*'] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Login to Docker Hub uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build and push uses: docker/build-push-action@v5 with: context: . push: true tags: | username/app:latest username/app:${{ github.sha }} cache-from: type=gha cache-to: type=gha,mode=max
``` ## Secrets and Environment Variables ### Adding Secrets 1. Go to repository Settings → Secrets and variables → Actions
2. Click "New repository secret"
3. Add name and value ### Using Secrets ```yaml
steps: - name: Deploy env: API_KEY: ${{ secrets.API_KEY }} DATABASE_URL: ${{ secrets.DATABASE_URL }} run: ./deploy.sh
``` ### Environment Variables ```yaml
env: NODE_ENV: production jobs: build: runs-on: ubuntu-latest env: CI: true steps: - name: Build env: BUILD_ID: ${{ github.run_id }} run: npm run build
``` ## Useful Features ### Caching Dependencies Speed up workflows by caching: ```yaml
- name: Cache node modules uses: actions/cache@v4 with: path: ~/.npm key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} restore-keys: | ${{ runner.os }}-node-
``` Or use built-in caching: ```yaml
- uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm' # Automatic caching
``` ### Matrix Builds Test across multiple configurations: ```yaml
strategy: matrix: os: [ubuntu-latest, windows-latest, macos-latest] node: [18, 20, 22] exclude: - os: macos-latest node: 18
``` ### Conditional Steps ```yaml
steps: - name: Deploy to staging if: github.ref == 'refs/heads/develop' run: ./deploy-staging.sh - name: Deploy to production if: github.ref == 'refs/heads/main' run: ./deploy-production.sh - name: Notify on failure if: failure() run: ./notify-team.sh
``` ### Artifacts Share files between jobs or download later: ```yaml
# Upload
- name: Upload build uses: actions/upload-artifact@v4 with: name: build path: dist/ # Download in another job
- name: Download build uses: actions/download-artifact@v4 with: name: build path: dist/
``` ### Manual Triggers ```yaml
on: workflow_dispatch: inputs: environment: description: 'Deployment environment' required: true default: 'staging' type: choice options: - staging - production jobs: deploy: runs-on: ubuntu-latest steps: - name: Deploy to ${{ inputs.environment }} run: ./deploy.sh ${{ inputs.environment }}
``` ### Scheduled Workflows ```yaml
on: schedule: - cron: '0 0 * * *' # Daily at midnight UTC - cron: '0 */6 * * *' # Every 6 hours
``` ## Popular Actions ### Checkout ```yaml
- uses: actions/checkout@v4 with: fetch-depth: 0 # Full history for versioning
``` ### Setup Languages ```yaml
# Node.js
- uses: actions/setup-node@v4 with: node-version: '20' # Python
- uses: actions/setup-python@v5 with: python-version: '3.12' # Go
- uses: actions/setup-go@v5 with: go-version: '1.22' # Java
- uses: actions/setup-java@v4 with: distribution: 'temurin' java-version: '21'
``` ### Deployment Actions ```yaml
# Vercel
- uses: amondnet/vercel-action@v25 with: vercel-token: ${{ secrets.VERCEL_TOKEN }} # Netlify
- uses: nwtgck/actions-netlify@v3 with: publish-dir: './dist' production-deploy: true # AWS S3
- uses: jakejarvis/s3-sync-action@master with: args: --delete env: AWS_S3_BUCKET: ${{ secrets.AWS_S3_BUCKET }}
``` ## Best Practices ### 1. Pin Action Versions ```yaml
# Good - pinned to specific version
- uses: actions/checkout@v4 # Better - pinned to commit SHA
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
``` ### 2. Fail Fast ```yaml
strategy: fail-fast: true # Stop all jobs if one fails matrix: node: [18, 20, 22]
``` ### 3. Set Timeouts ```yaml
jobs: build: runs-on: ubuntu-latest timeout-minutes: 10
``` ### 4. Use Concurrency Prevent duplicate runs: ```yaml
concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true
``` ### 5. Secure Secrets - Never log secrets
- Use OIDC for cloud providers when possible
- Limit secret scope to specific environments ### 6. Keep Workflows DRY Use reusable workflows: ```yaml
# .github/workflows/reusable-test.yml
on: workflow_call: inputs: node-version: required: true type: string jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: ${{ inputs.node-version }} - run: npm ci && npm test
``` Call it: ```yaml
jobs: test: uses: ./.github/workflows/reusable-test.yml with: node-version: '20'
``` ## Debugging Workflows ### Enable Debug Logging Add secret `ACTIONS_STEP_DEBUG` with value `true`. ### View Workflow Run 1. Go to repository → Actions tab
2. Click on workflow run
3. Expand job and steps to see output ### Local Testing Use [act](https://github.com/nektos/act) to run workflows locally: ```bash
# Install act
brew install act # Run workflow
act push
``` ## Pricing and Limits ### Free Tier - **Public repos:** Unlimited minutes
- **Private repos:** 2,000 minutes/month
- **Storage:** 500MB ### Included Minutes by Runner | Runner | Minutes Multiplier |
|--------|-------------------|
| Linux | 1x |
| Windows | 2x |
| macOS | 10x | ### Self-Hosted Runners Free alternative for private repos. Run on your own infrastructure. ## FAQ ### How do I trigger a workflow manually? Add `workflow_dispatch` to your `on` triggers. Then use the "Run workflow" button in the Actions tab. ### Can workflows access private packages? Yes, use secrets to store authentication tokens. GitHub also provides `GITHUB_TOKEN` for accessing the current repository. ### How do I pass data between jobs? Use artifacts or outputs: ```yaml
jobs: job1: outputs: version: ${{ steps.version.outputs.version }} steps: - id: version run: echo "version=1.0.0" >> $GITHUB_OUTPUT job2: needs: job1 steps: - run: echo ${{ needs.job1.outputs.version }}
``` ### Why is my workflow slow? Common causes:
- No dependency caching
- Too many sequential steps
- Large checkout (use sparse checkout)
- Slow tests (parallelize) ### Can I run workflows on a schedule? Yes, use cron syntax in the `schedule` event. Note: Scheduled workflows only run on the default branch. ## Conclusion GitHub Actions is a powerful CI/CD tool that integrates smoothly with your GitHub workflow. Start with simple workflows (test on push) and gradually add complexity (multi-environment deployments, scheduled tasks). **Getting started checklist:** 1. Create `.github/workflows/` directory
2. Add a simple test workflow
3. Verify it runs on push
4. Add deployment workflow
5. Configure secrets for production The investment in CI/CD pays off quickly through faster feedback, fewer bugs in production, and automated deployments that let you focus on writing code.

Advertisement

Share:
H

Written by HostScout Team

Author

Expert writer covering AI tools and software reviews. Helping readers make informed decisions about the best tools for their workflow.

Cite This Article

Use this citation when referencing this article in your own work.

HostScout Team. (2026, January 14). GitHub Actions Guide: CI/CD for Beginners. HostScout. https://hostscout.online/github-actions-ci-cd-guide-beginners/
HostScout Team. "GitHub Actions Guide: CI/CD for Beginners." HostScout, 14 Jan. 2026, https://hostscout.online/github-actions-ci-cd-guide-beginners/.
HostScout Team. "GitHub Actions Guide: CI/CD for Beginners." HostScout. January 14, 2026. https://hostscout.online/github-actions-ci-cd-guide-beginners/.
@online{github_actions_guide_2026,
  author = {HostScout Team},
  title = {GitHub Actions Guide: CI/CD for Beginners},
  year = {2026},
  url = {https://hostscout.online/github-actions-ci-cd-guide-beginners/},
  urldate = {March 17, 2026},
  organization = {HostScout}
}

Advertisement

Related Articles

Related Topics from Other Categories

You May Also Like