GitHub Actions & CI/CD Pipeline Mastery: The Complete Guide

In modern software engineering, Continuous Integration and Continuous Deployment (CI/CD) is not a luxury—it is the foundational nervous system of any high-performing engineering team. As of 2026, GitHub Actions has established absolute dominance over legacy systems like Jenkins, TravisCI, and CircleCI by integrating automation directly into the repository layer.

However, the gap between writing a basic npm run test script and engineering a Platinum Standard, enterprise-grade deployment pipeline is massive. Poorly configured workflows suffer from 20-minute build times, brittle tests, exorbitant billing costs, and critical security vulnerabilities through leaked secrets.

In this exhaustive, 3,500+ word technical guide, we will dissect GitHub Actions from the ground up. You will learn the core architectural primitives, advanced YAML matrix strategies, aggressive Docker layer caching, OpenID Connect (OIDC) security moats, and how to architect deployment pipelines that rival elite tech companies.


1. Core Architecture: Workflows, Jobs, and Steps

Before writing any YAML, you must understand the exact hierarchy and execution boundaries within GitHub Actions. A common mistake is confusing when variables are shared and when execution is isolated.

The Workflow

A Workflow is a configurable automated process defined by a YAML file placed strictly inside the .github/workflows/ directory of your repository. A repository can contain dozens of workflows (e.g., one for Pull Request linting, one for nightly Cron jobs, and one for Production Deployments). Workflows are triggered by Events.

Jobs and Runners

Inside a workflow, work is divided into Jobs. By default, every job in a workflow executes in parallel and completely independently on a fresh, pristine virtual machine called a Runner (such as ubuntu-latest, windows-latest, or macos-latest).

Because jobs run on physically isolated VMs, they do not share disk space or memory. If Job A compiles a React app into a dist/ folder, Job B cannot simply copy it. You must explicitly declare dependencies between jobs using the needs: [job_a] keyword (which forces sequential execution) and transfer files using the Artifacts API.

Steps and Actions

Within a Job, execution is broken down into sequential Steps. Steps run on the same Runner, meaning they share the same filesystem and memory namespace. A step can execute a raw shell script (using the run keyword) or invoke an Action (using the uses keyword).

Actions are pre-packaged, reusable units of code written by GitHub or the open-source community. For example, uses: actions/checkout@v4 is a standard action that securely clones your repository code onto the runner's disk, saving you from writing complex git clone and authentication scripts manually.

Example: The Architectural Hierarchy

name: Production Build

on:
  push:
    branches: ["main"]

jobs:
  build_app:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Code
        uses: actions/checkout@v4
      
      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: 20
          
      - name: Install & Build
        run: |
          npm ci
          npm run build
          
  deploy_app:
    needs: [build_app]
    runs-on: ubuntu-latest
    steps:
      - run: echo "Deploying..."

2. Mastering Event Triggers and Concurrency

Workflows execute in response to GitHub Events. While most developers know on: push, mastering enterprise pipelines requires granular control over exactly when and how workflows trigger.

Granular Push and PR Triggers

Running extensive end-to-end tests on every single commit is an excellent way to burn through your CI budget. You should aggressively filter triggers using paths and branches.

on:
  pull_request:
    branches: ["main", "staging"]
    paths:
      - 'src/**'
      - 'package.json'
    paths-ignore:
      - 'docs/**'
      - '**.md'

In this configuration, if a developer only updates a README.md file, the workflow instantly bypasses execution, saving minutes of compute time.

Manual Dispatches and Input Parameters

The workflow_dispatch event allows you to manually trigger a workflow via the GitHub UI, the GitHub CLI, or a Webhook payload. It is invaluable for one-off operations like database migrations, cache clearing, or rollback deployments.

on:
  workflow_dispatch:
    inputs:
      environment:
        description: 'Target Environment'
        required: true
        default: 'staging'
        type: choice
        options:
          - staging
          - production
      dry_run:
        description: 'Simulate deployment only'
        required: false
        type: boolean

Concurrency: Canceling Redundant Runs

If a developer pushes a commit, realizes a typo, and pushes a second commit 10 seconds later, GitHub Actions will traditionally spawn two parallel workflows. The first workflow is now obsolete, but it will continue consuming compute minutes and might even cause deployment race conditions.

The concurrency group entirely eliminates this problem. By grouping runs based on the Git ref (branch name), GitHub will automatically cancel any in-progress runs when a new push occurs.

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

3. Dynamic Matrices and Parallel Execution

When building open-source libraries or cross-platform desktop applications, ensuring compatibility across multiple environments is critical. Writing separate jobs for Ubuntu, Windows, and macOS across Node.js 18, 20, and 22 would require thousands of lines of duplicated YAML.

The Strategy Matrix

The strategy.matrix acts as a multiplier. By supplying arrays of variables, GitHub computes the Cartesian product and spawns a unique parallel job for every combination.

jobs:
  test:
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        node-version: [18.x, 20.x, 22.x]
        
    steps:
      - uses: actions/checkout@v4
      - name: Use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
      - run: npm ci
      - run: npm test

This simple block generates 9 simultaneous jobs. The fail-fast: false directive ensures that if the Windows/Node 18 job fails, the remaining 8 jobs continue executing to completion, allowing you to isolate platform-specific bugs accurately.

Matrix Exclusions and Additions

Sometimes, certain combinations are invalid or unsupported. You can prune the matrix using exclude or inject highly specific single combinations using include.

    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest]
        node: [18, 20]
        exclude:
          # Windows does not support our Node 18 native bindings
          - os: windows-latest
            node: 18
        include:
          # Test a bleeding-edge nightly build explicitly on Ubuntu
          - os: ubuntu-latest
            node: 23-nightly

4. Caching, Artifacts, and Build Speed Optimization

Slow pipelines cripple developer velocity. If a CI run takes 25 minutes, developers lose focus and context-switch, creating massive productivity drains. The primary culprits behind slow builds are network I/O (downloading the same npm modules repeatedly) and CPU bottlenecks (recompiling unchanged native binaries or Docker layers).

NPM & Dependency Caching

Every runner starts completely blank. Running npm install downloads hundreds of megabytes of identical packages on every run. Implementing the actions/cache mechanism allows you to save the node_modules folder to GitHub's internal fast-storage and restore it in seconds.

steps:
  - uses: actions/checkout@v4
  
  - name: Cache Node Modules
    uses: actions/cache@v4
    id: npm-cache
    with:
      path: ~/.npm
      key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
      restore-keys: |
        ${{ runner.os }}-node-
        
  - name: Install Dependencies
    run: npm ci

The key utilizes a hash of the package-lock.json file. If your dependencies remain unchanged, the hash matches the cache key, and the folder is instantly downloaded. If a developer adds a new package, the hash changes, resulting in a cache miss and a fresh installation, followed by a new cache upload at the end of the run.

Note: Modern setup actions like setup-node, setup-python, and setup-go now feature built-in caching simply by adding cache: 'npm' to their parameters, largely replacing the need for raw manual cache configurations.

Docker Layer Caching (DLC)

If your deployment pipeline relies on building Docker containers, compiling the image from scratch every time is devastatingly slow. Docker natively supports caching intermediate build layers, and GitHub Actions can utilize external registries or the specialized gha (GitHub Actions) cache backend.

- name: Set up Docker Buildx
  uses: docker/setup-buildx-action@v3

- name: Build and Push Docker Image
  uses: docker/build-push-action@v5
  with:
    context: .
    push: true
    tags: my-registry.com/app:latest
    cache-from: type=gha
    cache-to: type=gha,mode=max

The type=gha parameter instructs Buildx to export all intermediate Docker layers directly into the GitHub Actions cache API. The mode=max ensures that even intermediate layers that weren't part of the final image output are cached, resulting in hyper-optimized sub-minute builds. For deeper Docker insights, review our Complete Docker Compose Guide.

Artifacts: Cross-Job File Transfers

When Job A creates a production .zip bundle and Job B deploys it, you cannot rely on the filesystem. Artifacts are the official mechanism for archiving outputs from a runner.

# Inside Job A (Build)
- name: Upload Build Output
  uses: actions/upload-artifact@v4
  with:
    name: production-dist
    path: dist/
    retention-days: 5

# Inside Job B (Deploy)
- name: Download Build Output
  uses: actions/download-artifact@v4
  with:
    name: production-dist
    path: deploy-folder/

5. Enterprise Security: OIDC, Environments, and Secret Scanners

CI/CD systems hold the keys to the kingdom. If an attacker gains access to your GitHub Actions environment, they can steal production database passwords, AWS credentials, and deployment keys. Hardening your pipeline is non-negotiable.

The Fall of Long-Lived Credentials

Historically, deploying to AWS required creating an IAM User, generating a permanent AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY, and storing them in GitHub Secrets. If these keys were ever accidentally logged or exfiltrated, attackers had permanent access until manual rotation occurred.

The Rise of OpenID Connect (OIDC)

OIDC radically shifts the security paradigm. Instead of storing permanent keys, GitHub Actions operates as an Identity Provider. AWS, Azure, and GCP are configured to trust GitHub's cryptographic signatures. When a workflow runs, GitHub generates a signed JSON Web Token (JWT) asserting the repository name and branch.

The workflow presents this JWT to AWS. AWS cryptographically verifies the token and issues a temporary, short-lived session token valid for only 60 minutes.

permissions:
  id-token: write   # Required to generate OIDC JWT
  contents: read    # Required to clone the repository

steps:
  - name: Configure AWS Credentials via OIDC
    uses: aws-actions/configure-aws-credentials@v4
    with:
      role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsDeployRole
      aws-region: us-east-1
      
  - name: Deploy to S3
    run: aws s3 sync ./dist s3://my-production-bucket

By implementing OIDC, there are zero static secrets stored in GitHub. Even if the workflow execution is fully compromised, the attacker only acquires a temporary token that expires before they can successfully exploit the broader infrastructure.

GitHub Environments and Protection Rules

For production deployments, pushing to the main branch should not trigger an automatic deploy without human oversight. GitHub Environments provide approval gates and environment-specific secrets.

jobs:
  deploy_production:
    runs-on: ubuntu-latest
    environment:
      name: production
      url: https://www.mywebsite.com
    steps: ...

When this job initiates, it pauses execution. A notification is sent to the engineering managers. Only after a human clicks "Approve" in the GitHub UI will the job receive the production secrets and proceed with the deployment.

Preventing Secret Leakage

Even with GitHub's automatic secret masking (which replaces printed secrets with *** in logs), developers can inadvertently expose secrets by echoing them into files or base64 encoding them. Always utilize our local Secret Scanner to pre-validate source code, and strictly limit the permissions block inside workflows to adhere to the Principle of Least Privilege.


6. Advanced Deployment Patterns: SSH, SSH Tunnels, and Webhooks

While modern stacks utilize Kubernetes and declarative GitOps via ArgoCD, thousands of reliable enterprises still rely on traditional VM deployments utilizing SSH and Systemd. Automating these pipelines via GitHub Actions requires precision.

Zero-Downtime Deployments via SSH

Deploying directly to a Linux server often involves copying files, restarting a Systemd service, and reloading Nginx. Executing this securely requires utilizing GitHub Secrets for your SSH Keys.

- name: Secure Rsync and Restart
  uses: appleboy/[email protected]
  with:
    host: ${{ secrets.PROD_HOST }}
    username: deployer
    key: ${{ secrets.DEPLOY_SSH_KEY }}
    script: |
      cd /var/www/app
      git pull origin main
      npm ci --production
      sudo systemctl restart my-app.service
      sudo nginx -s reload

Crucial Security Note: Never run deployment commands as the root user. Create a dedicated deployer user, configure highly restrictive Linux ACLs, and use the visudo file to grant passwordless sudo access only for specific systemctl commands.

VPNs, Jump Hosts, and Network Isolation

Production servers should never expose Port 22 (SSH) to the public internet. If your infrastructure resides in a private AWS VPC, GitHub's public IP addresses cannot reach it directly.

To solve this, modern pipelines deploy via a Bastion server utilizing SSH ProxyJump, or they establish a temporary VPN tunnel (like Tailscale or WireGuard) directly inside the runner VM prior to executing the deployment steps.

- name: Connect to Tailscale VPN
  uses: tailscale/github-action@v2
  with:
    oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
    oauth-secret: ${{ secrets.TS_OAUTH_SECRET }}
    tags: tag:ci

- name: Deploy to Private Subnet Server
  run: ssh [email protected] 'bash ./update.sh'

7. Cost Optimization and Self-Hosted Runners

GitHub provides a generous free tier of actions minutes, but enterprise repositories running heavy integration tests, Docker builds, and matrix jobs can incur thousands of dollars in monthly compute fees. The default ubuntu-latest runners utilize modest 2-core / 7GB RAM virtual machines. If your application takes 40 minutes to compile on these machines, you are severely bleeding capital.

The Self-Hosted Revolution

GitHub allows you to register your own virtual machines or bare-metal servers as Self-Hosted Runners. You pay absolute zero to GitHub for compute minutes; you only pay your cloud provider for the raw infrastructure.

By spinning up a $40/month Hetzner dedicated server featuring 16 CPU cores and 64GB of RAM, you can slash compilation times from 40 minutes down to 3 minutes, while completely neutralizing GitHub's per-minute billing fees.

Security Risks of Self-Hosted Runners

Self-hosted runners maintain persistent local state. Unlike GitHub-hosted runners, which are destroyed and recycled after every single job, a bare-metal self-hosted runner executes Job B in the exact same environment that Job A just finished in.

If a malicious pull request injects code that writes a backdoor into the /tmp/ directory, subsequent jobs from other branches might execute that backdoor. Therefore, never execute untrusted pull requests from public forks on self-hosted runners. Reserve them exclusively for private repositories, highly trusted internal teams, and controlled production branches.


8. Diagnostics, Debugging, and Pipeline Best Practices

When a pipeline fails, developers often resort to "shotgun debugging"—committing random speculative fixes featuring commit messages like "fix tests", "fix again", "maybe this works." This clutters the git history and wastes immense time.

Enable Runner Diagnostic Logging

If an action fails silently or a bash script behaves unpredictably, you can force the runner to expose exhaustive internal telemetry. Navigate to your repository settings and create a secret named ACTIONS_STEP_DEBUG with the value true. The next workflow run will dump thousands of lines of deep diagnostic traces, highlighting exactly where environment variables and memory states derailed.

Local Debugging with Act

To prevent polluting your commit history, utilize the open-source tool Act. Act runs a specialized Docker container on your local workstation that perfectly mimics the GitHub Actions runtime environment.

# Install act via Homebrew
brew install act

# Run the 'push' event workflows locally
act push

# Run a specific job
act -j build_app

Testing complex workflows locally inside Docker guarantees rapid feedback loops without waiting for GitHub's queuing infrastructure.

Maintain DRY Workflows with Reusable Pipelines

If your organization contains 50 microservices, maintaining 50 separate .github/workflows/deploy.yml files violates the DRY (Don't Repeat Yourself) principle. When a security protocol changes, updating 50 repositories manually is catastrophic.

Utilize Reusable Workflows (workflow_call). Define the master deployment logic centrally in an infrastructure repository. The 50 microservices simply reference the master workflow and pass in local parameters.

# Microservice repository
jobs:
  call-master-deploy:
    uses: MyOrg/infrastructure/.github/workflows/[email protected]
    with:
      app_name: "payment-gateway"
    secrets: inherit

9. Infrastructure as Code: Terraform and GitHub Actions

Deploying code is only half of the Continuous Deployment equation. Modern DevOps requires deploying the infrastructure that runs the code alongside it. Integrating Terraform (or OpenTofu) directly into GitHub Actions allows teams to manage AWS VPCs, ECS Clusters, and RDS databases via pull requests (often called GitOps).

The Terraform Plan and Apply Workflow

The golden rule of Infrastructure as Code (IaC) in CI/CD is strict separation of the plan phase and the apply phase. A Terraform Plan calculates exactly what cloud resources will be created, modified, or destroyed. This plan must be attached directly to the Pull Request for human review before any destructive changes occur on the main branch.

name: Terraform CI/CD

on:
  push:
    branches: ["main"]
  pull_request:

jobs:
  terraform_plan:
    if: github.event_name == 'pull_request'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: hashicorp/setup-terraform@v3
      - run: terraform init
      - run: terraform plan -out=tfplan
      
      # Upload the plan artifact for review
      - uses: actions/upload-artifact@v4
        with:
          name: tf-plan
          path: tfplan

PR Commenting and Automation

Reviewing raw Terraform logs inside the GitHub Actions console is a terrible developer experience. Advanced teams utilize the actions/github-script action to parse the output of terraform plan and automatically inject it as a formatted comment directly onto the Pull Request timeline.

This ensures that when a developer reviews the code, they immediately see that the PR will "Destroy 1 RDS Database and Create 2 S3 Buckets" right next to the code diff, preventing catastrophic infrastructure deletions.

10. CI/CD Strategies for Massive Monorepos

As organizations scale, many migrate toward monorepos (storing the frontend, backend, and infrastructure in a single giant repository) using tools like Nx, Turborepo, or Bazel. Monorepos utterly break naive GitHub Actions configurations because a single push will trigger testing and building for every microservice, even if only the frontend UI was changed.

Path Filtering at Scale

The first defense mechanism is aggressive path filtering. Instead of a single monolithic ci.yml, you create dedicated workflows for each microservice inside .github/workflows/ and scope them to specific directories.

name: Backend CI
on:
  pull_request:
    paths:
      - 'apps/backend/**'
      - 'packages/shared-types/**'
      
jobs:
  test_backend:
    runs-on: ubuntu-latest
    steps: ...

This ensures that if a developer alters a React component inside apps/frontend, the Backend CI workflow simply never executes, saving immense time and money.

Dynamic Matrix Generation

When dealing with 50+ microservices, writing 50 workflow files becomes an architectural nightmare. The platinum standard for monorepos involves dynamic matrix generation. In this pattern, Job A analyzes the Git commit diff to determine exactly which packages were modified (using tools like nx affected). Job A then outputs a JSON array of the affected projects.

Job B then ingests that JSON array into its strategy.matrix configuration, dynamically spawning parallel jobs only for the microservices that actually need building.

jobs:
  detect_changes:
    runs-on: ubuntu-latest
    outputs:
      matrix: ${{ steps.set-matrix.outputs.matrix }}
    steps:
      - run: echo "matrix=[\"backend-api\", \"auth-service\"]" >> $GITHUB_OUTPUT
      
  build_affected:
    needs: [detect_changes]
    strategy:
      matrix:
        service: ${{ fromJSON(needs.detect_changes.outputs.matrix) }}
    steps:
      - run: npm run build --workspace=${{ matrix.service }}

This represents the absolute pinnacle of GitHub Actions engineering—pipelines that are infinitely scalable, entirely dynamic, and fiercely cost-optimized.


11. The 2026 Landscape: GitHub Actions vs. Jenkins vs. GitLab CI

For enterprise architecture teams, migrating a massive CI/CD pipeline from a legacy system to GitHub Actions is a multi-million dollar decision. Understanding the exact architectural differences, security boundaries, and total cost of ownership between GitHub Actions, Jenkins, and GitLab CI is critical for DevOps leaders.

The Fall of Jenkins: The Maintenance Nightmare

Jenkins was the undisputed king of CI/CD for over a decade. However, by 2026, it is widely considered a legacy burden. Jenkins relies on a centralized master server that requires constant patching, JVM tuning, and plugin updates. If the Jenkins master crashes, the entire engineering organization is paralyzed. Furthermore, Jenkins pipelines are often defined in imperative Groovy scripts, which are incredibly difficult to lint, test, and maintain compared to declarative YAML.

GitHub Actions entirely abstracts away the control plane. GitHub manages the master orchestration, the queuing system, and the webhooks. Your team only manages the declarative YAML and, optionally, the compute nodes (Self-Hosted Runners). The reduction in infrastructure maintenance overhead is staggering, often saving companies entire dedicated DevOps headcount.

GitLab CI: The Closest Competitor

GitLab CI remains the only true competitor to GitHub Actions in 2026. Both are integrated directly into the Git repository, both use declarative YAML, and both support runners. GitLab historically held a slight edge in complex Directed Acyclic Graph (DAG) pipeline orchestration.

However, GitHub Actions has pulled ahead significantly due to the GitHub Marketplace. The open-source ecosystem of pre-built Actions (uses: ...) is vast. Whether you need to deploy to AWS ECS, upload coverage to Codecov, scan for secrets with Trivy, or post a message to Slack, there is a battle-tested, community-maintained Action ready to use in one line of code. In GitLab, teams often have to write bespoke bash scripts or build custom Docker images to achieve the exact same integrations.

12. Engineering Custom Actions: TypeScript and Docker

Eventually, your team will encounter a proprietary workflow that cannot be solved using the GitHub Marketplace. Perhaps you need to query an internal LDAP server, trigger a proprietary hardware testing suite, or parse a highly specific XML report format. When shell scripts become too brittle, it is time to author a Custom GitHub Action.

Types of Custom Actions

GitHub supports three primary methods for creating custom actions: Composite Actions, JavaScript (TypeScript) Actions, and Docker Container Actions.

Composite Actions: Bundling YAML

Composite actions are the simplest to create. If you find yourself copying and pasting the exact same five run: steps across multiple workflows (e.g., installing a specific CLI tool, configuring a credential, and running a health check), you can bundle them into a single action.yml file.

# action.yml
name: 'Setup Corporate CLI'
description: 'Downloads and configures the internal CLI'
runs:
  using: "composite"
  steps:
    - run: curl -O https://internal.corp/cli.tar.gz
      shell: bash
    - run: tar -xzf cli.tar.gz -C /usr/local/bin
      shell: bash

Other repositories can now invoke this logic cleanly with uses: MyOrg/setup-cli@v1, drastically reducing YAML boilerplate across the organization.

TypeScript Actions: The Enterprise Standard

When you need complex conditional logic, API calls, or heavy string parsing, bash scripts fail. TypeScript Actions are executed directly by the runner's pre-installed Node.js binary. By utilizing the @actions/core and @actions/github npm packages, you gain native, strongly-typed access to the workflow's inputs, outputs, and the authenticated GitHub REST API (Octokit).

import * as core from '@actions/core';
import * as github from '@actions/github';

async function run(): Promise<void> {
  try {
    const token = core.getInput('github_token');
    const octokit = github.getOctokit(token);
    const context = github.context;

    if (context.eventName === 'pull_request') {
      await octokit.rest.issues.createComment({
        ...context.repo,
        issue_number: context.issue.number,
        body: 'Custom TS Action executed successfully! 🚀'
      });
    }
  } catch (error) {
    if (error instanceof Error) core.setFailed(error.message);
  }
}

run();

TypeScript actions execute in milliseconds because they do not require spinning up new Docker containers, making them the platinum standard for custom CI/CD logic. The code must be compiled into a single file (using @vercel/ncc) before pushing to the repository.

Docker Container Actions: Maximum Isolation

If your action requires a specific Linux distribution, exotic system dependencies (like Rust or C++ compilers), or legacy binaries, you should build a Docker Container Action. The action.yml simply points to a Dockerfile. When the workflow runs, GitHub automatically builds the Docker image and executes your code inside the container, mapping the repository workspace into the container volume automatically.

While Docker actions provide absolute isolation and reproducibility, they suffer a performance penalty, as the runner must build or pull the image before execution can begin.


The Future of Automation

GitHub Actions has democratized elite, hyperscale CI/CD infrastructure, making it available to individual developers and massive enterprises alike. By mastering the core execution boundaries, dynamic matrix permutations, Docker caching mechanisms, and zero-trust OIDC security integrations detailed in this massive guide, you possess the capability to construct pipelines that execute flawlessly in seconds rather than hours.

To continue expanding your DevOps mastery, explore our architectural deep-dives on Docker Compose, Nginx Reverse Proxies, and secure SSH Systems. For real-time configuration generation, leverage the ZeroData locally-processed utilities linked throughout this ecosystem, such as our Docker Env Mapper and Cron Generator.

How ZeroData protects your privacy

  • No Uploads: Processing happens entirely via client-side JavaScript.
  • No Storage: We do not have a database. We physically cannot save your data.
  • No Tracking: We don't log what you process or track your inputs.
  • Verifiable: Check your DevTools Network tab. You will see 0 outbound requests.

Frequently Asked Questions

What is the difference between a GitHub Action, a Step, and a Job?

A workflow is the overarching automated process. Workflows contain one or more jobs, which run in parallel by default. Each job executes on its own runner (VM) and consists of a sequence of steps. A step can either run a shell command directly or execute an Action, which is a pre-packaged, reusable script (like checking out code or setting up Node.js).

How do I share data between jobs in GitHub Actions?

Because jobs run on separate virtual machines, you cannot pass variables directly in memory. You must use the `actions/upload-artifact` action in the producing job to upload files (like build binaries) to GitHub's storage, and then use `actions/download-artifact` in the consuming job. Alternatively, for simple strings, you can use job outputs.

What is OIDC and why is it better than storing AWS/GCP credentials in GitHub Secrets?

OpenID Connect (OIDC) establishes a trust relationship between GitHub and your cloud provider. Instead of pasting long-lived, permanent AWS Access Keys into GitHub Secrets (which can be leaked or stolen), OIDC allows your GitHub workflow to request a temporary, short-lived access token valid only for the duration of the run. This eliminates credential rotation and greatly enhances security.

How does the strategy matrix work?

The `strategy.matrix` keyword allows you to run the same job multiple times across different variable combinations. For example, testing across `node-version: [18, 20, 22]` and `os: [ubuntu-latest, windows-latest]`. GitHub will automatically spawn 6 parallel jobs to cover every combination.

How can I reduce my GitHub Actions billing costs?

Use heavy caching (`actions/cache`) to avoid downloading NPM packages or Docker layers repeatedly. Utilize `concurrency` groups to automatically cancel obsolete in-progress runs when a developer pushes new commits. For heavy workloads, switch to self-hosted runners on your own AWS EC2 or Hetzner instances where compute is dramatically cheaper than GitHub's per-minute VM pricing.