{"post":{"slug":"how-to-deploy-with-github-actions-2026","title":"How to deploy with GitHub Actions in 2026","excerpt":"GitHub Actions is the default CI/CD choice for most teams in 2026. This guide covers fast, secure deployment pipelines that actually reach production safely.","category":"code","date":"March 12, 2026","author":"ByteLedger Team","content":"<span class=\"dropcap\">G</span>itHub Actions ships with every repository, runs on managed infrastructure, and integrates with every major cloud. By 2026 it has become the default CI/CD choice for teams that do not want to maintain a separate Jenkins or Buildkite cluster. But \"it works\" and \"it works safely at production scale\" are very different things. This guide covers the gap.\n\n## What changed in 2026\n\n- **OIDC is the standard for cloud auth.** AWS, GCP, and Azure all support GitHub OIDC federation. Teams that still use static `AWS_ACCESS_KEY_ID` secrets are taking unnecessary risk and will find their security audits flagging it.\n- **Actions cache v4** handles concurrent writes gracefully and is keyed on lockfile hash out of the box. Cache hit rates above 90% are routine.\n- **Larger runners are now cost-effective.** GitHub-hosted `ubuntu-24.04-8core` runners (4× CPU, 8× RAM) cut Docker build times by 3–4× versus `ubuntu-latest` for a modest price premium.\n- **Merge queues went GA.** Branch protection with merge queues serializes PRs, so you stop seeing \"passed CI but broke main after merge.\"\n\n## A minimal, correct deploy workflow\n\n```yaml\n# .github/workflows/deploy.yml\nname: Deploy\n\non:\n  push:\n    branches: [main]\n\npermissions:\n  id-token: write   # required for OIDC\n  contents: read\n\njobs:\n  build:\n    runs-on: ubuntu-24.04\n    outputs:\n      image: ${{ steps.meta.outputs.tags }}\n    steps:\n      - uses: actions/checkout@v4\n      - name: Set up Docker Buildx\n        uses: docker/setup-buildx-action@v3\n      - name: Log in to GHCR\n        uses: docker/login-action@v3\n        with:\n          registry: ghcr.io\n          username: ${{ github.actor }}\n          password: ${{ secrets.GITHUB_TOKEN }}\n      - name: Extract metadata\n        id: meta\n        uses: docker/metadata-action@v5\n        with:\n          images: ghcr.io/${{ github.repository }}\n      - name: Build and push\n        uses: docker/build-push-action@v6\n        with:\n          push: true\n          tags: ${{ steps.meta.outputs.tags }}\n          cache-from: type=gha\n          cache-to: type=gha,mode=max\n\n  deploy-staging:\n    needs: build\n    runs-on: ubuntu-24.04\n    environment: staging\n    steps:\n      - uses: aws-actions/configure-aws-credentials@v4\n        with:\n          role-to-assume: arn:aws:iam::123456789:role/github-deploy-staging\n          aws-region: us-east-1\n      - run: |\n          aws ecs update-service \\\n            --cluster staging \\\n            --service api \\\n            --force-new-deployment\n\n  deploy-production:\n    needs: deploy-staging\n    runs-on: ubuntu-24.04\n    environment: production   # requires reviewer approval\n    steps:\n      - uses: aws-actions/configure-aws-credentials@v4\n        with:\n          role-to-assume: arn:aws:iam::123456789:role/github-deploy-prod\n          aws-region: us-east-1\n      - run: |\n          aws ecs update-service \\\n            --cluster production \\\n            --service api \\\n            --force-new-deployment\n```\n\n## OIDC authentication (no static secrets)\n\nCreate a trust policy in AWS that allows the Actions token for your repository:\n\n```json\n{\n  \"Effect\": \"Allow\",\n  \"Principal\": {\n    \"Federated\": \"arn:aws:iam::123456789:oidc-provider/token.actions.githubusercontent.com\"\n  },\n  \"Action\": \"sts:AssumeRoleWithWebIdentity\",\n  \"Condition\": {\n    \"StringLike\": {\n      \"token.actions.githubusercontent.com:sub\":\n        \"repo:your-org/your-repo:environment:production\"\n    }\n  }\n}\n```\n\nThe `sub` condition scopes the trust to a specific environment, so the production role cannot be assumed by a staging job or a PR build.\n\n## Dependency caching\n\n```yaml\n- uses: actions/setup-node@v4\n  with:\n    node-version: '22'\n    cache: 'pnpm'          # hashes pnpm-lock.yaml automatically\n\n- uses: actions/setup-python@v5\n  with:\n    python-version: '3.13'\n    cache: 'pip'           # hashes requirements.txt / pyproject.toml\n```\n\nFor Docker layers, `cache-from: type=gha` (shown above) stores layer blobs in Actions cache and restores them on the next run. Combined with multi-stage builds, this cuts image build times from ~4 minutes to under 60 seconds on cache hits.\n\n## How to structure environments\n\n| Environment | Trigger | Gate | Secrets scope |\n| --- | --- | --- | --- |\n| preview | PR open | none (auto) | test credentials only |\n| staging | push to main | none (auto) | staging credentials |\n| production | staging passes | required reviewer | prod credentials |\n\nSet this up under **Settings → Environments** in the GitHub UI, not in workflow YAML. The environment protection rules (required reviewers, deployment branch restrictions) are enforced by GitHub, not by YAML you can accidentally delete.\n\n## How to pick the right runner\n\n| Workload | Runner |\n| --- | --- |\n| Lint, unit tests | `ubuntu-latest` (2-core, free) |\n| Docker build, integration tests | `ubuntu-24.04-4core` |\n| Large monorepo, heavy build | `ubuntu-24.04-8core` |\n| macOS app build | `macos-15` (billed separately) |\n| GPU model inference tests | Self-hosted with GPU |\n\nPaying ~3× for a 4-core runner that finishes in a quarter of the time is usually cost-neutral and improves developer experience.\n\n## Common mistakes\n\n**Checking out code in every job.** Only the jobs that need source code should run `actions/checkout`. Downstream deploy jobs just need the image tag from `outputs`.\n\n**Storing deploy credentials as repository secrets instead of environment secrets.** Repository secrets are accessible to every job, including PR builds from forks. Environment secrets are scoped.\n\n**No rollback plan.** Every deploy workflow should have a `rollback` workflow or a manual trigger that re-deploys the previous image tag. ECS, Kubernetes, and most PaaS platforms support this natively.\n\n**Running tests and deploy in the same job.** Separate concerns. Tests run on every push; deploy runs only when tests pass on main.\n\n## What to skip\n\n- **Self-hosted runners for simple apps** — managed runners are easier to maintain and the cost difference is small.\n- **Workflow files with 300+ lines** — extract logic into composite actions or reusable workflows; long YAML is unreadable.\n- **Deploying to production on every PR merge without staging** — even a 5-minute smoke test on staging catches the majority of regressions.\n\n## FAQ\n\n**How do I pass an image tag between jobs?**\nUse `outputs` on the build job and reference it with `${{ needs.build.outputs.image }}` in downstream jobs (as shown in the example above).\n\n**Can I run the deploy workflow locally?**\nUse `act` (github.com/nektos/act) to run workflows locally. It does not perfectly replicate the GitHub environment but catches most YAML syntax and logic errors before push.\n\n**How do I handle database migrations in the pipeline?**\nRun migrations as a step in the staging deploy job before the new containers start. Use a migration tool (Flyway, Alembic, golang-migrate) that supports versioned, idempotent migrations and can roll back if the health check fails.\n\n**How do I limit who can trigger a production deployment?**\nSet required reviewers in the environment settings. Only those users can approve the pending deployment. Combine with deployment branch restrictions so only `main` can deploy to production.\n\n## Where to go next\n\n- [How to set up CI/CD in 2026](/blog/how-to-set-up-ci-cd-2026)\n- [How to write integration tests in 2026](/blog/how-to-write-integration-tests-2026)\n- [How to handle secrets in 2026](/blog/how-to-handle-secrets-2026)","hue":306,"metaTitle":"How to Deploy with GitHub Actions in 2026","metaDescription":"Step-by-step guide to deploying with GitHub Actions in 2026. Covers OIDC auth, environment gates, caching, and production-safe pipeline patterns.","keywords":["how to deploy with github actions 2026","github actions deployment 2026","github actions ci cd tutorial","github actions oidc aws","github actions environments secrets","github actions cache 2026","github actions workflow best practices","continuous deployment github actions","github actions docker deploy","github actions production deployment"],"tldr":["**Use environments with protection rules** — staging and production environments gate secrets and require reviewer approval before deployment.","**Cache aggressively** — caching node_modules or pip wheels cuts job time by 60–80%; Actions cache v4 is keyed on lockfile hash by default.","**Prefer OIDC over static secrets** — GitHub OIDC tokens let AWS, GCP, and Azure issue short-lived credentials without storing long-lived keys.","**Separate build from deploy** — build once, promote the same artifact through staging to production; never rebuild per environment.","**Skip:** putting deploy logic in massive shell scripts inside YAML — extract to a Makefile or script file you can test locally."],"readTime":"5 min read","wordCount":996},"related":[{"slug":"how-to-write-a-github-action-2026","title":"How to write a GitHub Action in 2026: CI workflows, secrets, and reuse","excerpt":"GitHub Actions is the de facto CI/CD platform for open-source and most private projects. This guide covers writing workflows, managing secrets, and reusing logic.","category":"code","date":"March 1, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":311},{"slug":"how-to-set-up-ci-cd-2026","title":"How to set up CI/CD in 2026: a practical pipeline from zero to deploy","excerpt":"A CI/CD pipeline that catches bugs before production is one of the highest-ROI investments a team can make. This 2026 guide walks through the full setup.","category":"code","date":"February 26, 2026","author":"ByteLedger Team","readTime":"5 min read","hue":311},{"slug":"ci-cd-pipeline-basics-2026","title":"CI/CD pipeline basics in 2026: build, test, and deploy without fear","excerpt":"A well-designed CI/CD pipeline turns deploys from a stressful event into a boring routine. Here is the 2026 baseline every engineering team needs.","category":"code","date":"February 20, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":314}],"sameCategory":[{"slug":"bazel-build-explained-2026","title":"Bazel Explained: What Hermetic Builds Actually Cost","excerpt":"Bazel delivers reproducible, precisely cached builds and demands that you describe everything. Here is what you get, what it costs, and who should say no.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":145},{"slug":"graphql-federation-2026","title":"GraphQL Federation: One Graph, Many Teams","excerpt":"Federation composes independently-owned subgraphs into a single API. Here is what it solves, the operational cost it adds, and when a simpler gateway is enough.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":300},{"slug":"hermetic-builds-explained-2026","title":"Hermetic Builds Explained: Same Inputs, Same Output","excerpt":"A hermetic build depends only on declared inputs. Here is what that property gives you, the common leaks that break it, and how to move toward it incrementally.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":130},{"slug":"monorepo-tooling-compared-2026","title":"Monorepo Tooling Compared: What You Actually Need","excerpt":"Monorepo tools solve task orchestration and caching, not repository layout. Here is what each tier provides and how to tell when you have outgrown the simple option.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":150}],"newer":{"slug":"how-to-deploy-to-vercel-2026","title":"How to deploy to Vercel in 2026: the complete guide","excerpt":"Vercel remains the fastest path from git push to live app for most frontend and full-stack projects. This guide covers every step correctly for 2026.","category":"code","date":"March 12, 2026","author":"ByteLedger Team","readTime":"5 min read","hue":320},"older":{"slug":"how-to-descale-a-kettle-2026","title":"How to descale a kettle in 2026: fast, cheap, and thorough","excerpt":"Limescale builds up in every kettle and slows boiling while adding a chalky taste to hot drinks. This 2026 guide covers the fastest descaling methods and how to prevent scale.","category":"guides","date":"March 12, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":290}}