Skip to content

CodePipeline vs GitHub Actions: when CodePipeline earns its keep

CodePipeline wins on IAM-native cross-account deploys, residency, CodeDeploy blue/green. GH Actions wins on iteration speed and ecosystem. The honest middle ground: GH Actions for CI, CodePipeline for deploy.

A founder has built the MVP on GitHub Actions because that is what was there. The first paying customer is a Quebec health-data buyer who asks where build artifacts are stored, which IAM principal can read them, and whether the production deploy can be initiated by anyone with a GitHub PAT. The team is in the middle of evaluating whether to migrate the whole CI/CD to CodePipeline, keep everything in GH Actions, or split the two. The right answer is rarely “all of one.” Here is the honest read on where each tool wins, and the split that holds up for most regulated SaaS in Canada.

What’s happening

GitHub Actions and CodePipeline solve overlapping problems with different center-of-gravity:

  • GitHub Actions is a fast, ecosystem-rich CI/CD tool with the source-of-truth (the Git repo) tightly integrated. PR checks, branch protection, matrix builds, and the marketplace of community actions all live in the same place as the code. The execution runners are GitHub-hosted by default (multi-tenant runners on Microsoft Azure infrastructure), or self-hosted in your own account.
  • CodePipeline is an AWS-native orchestration service. The execution model is “stages of actions, each action is an AWS service (CodeBuild, CodeDeploy, Lambda, CloudFormation, manual approval).” IAM is the access control layer. The runners are CodeBuild compute, which runs in your account.

For most decisions, the question is not “which is better” but “which axis matters most.” Five axes, and the tool that wins on each:

AxisGH ActionsCodePipeline
Iteration speedwins
Open-source ecosystemwins
Free for public reposwins
IAM-native cross-account deploywins
Data residency in ca-central-1wins
CodeDeploy blue/green nativewins
Approval workflows for prodwins
AWS service variety for orchestrationwins

The middle ground that works for most teams: GH Actions runs CI (lint, test, build the artifact, push to a container registry). CodePipeline picks up the artifact via an ECR event or an S3 event, then runs the deploy stages — cross-account, blue/green, manual approval, post-deploy validation. This split takes the strength of each tool: GH Actions stays close to the PR, CodePipeline stays close to AWS.

How to verify

The “where does the artifact live and who can touch it” question is the most concrete way to compare the two tools.

GitHub Actions, default setup: the workflow runs on a GitHub-hosted runner, builds the artifact, and pushes it somewhere. To push to AWS (ECR, S3), the workflow needs AWS credentials. The naive pattern is an AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY stored as repository secrets. This is a long-lived credential available to anyone who can run a workflow on the repo — every contributor with write access, every action in the workflow file. The audit objection writes itself.

The correct pattern is OIDC federation: configure the GitHub Actions OIDC provider as an IAM Identity Provider in AWS, set up a role with a trust policy that trusts the OIDC provider scoped to specific repos and branches, and have the workflow assume the role at runtime with aws-actions/configure-aws-credentials. The trust policy looks like:

{
  "Effect": "Allow",
  "Principal": {
    "Federated": "arn:aws:iam::111111111111:oidc-provider/token.actions.githubusercontent.com"
  },
  "Action": "sts:AssumeRoleWithWebIdentity",
  "Condition": {
    "StringEquals": {
      "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
    },
    "StringLike": {
      "token.actions.githubusercontent.com:sub": "repo:stackharbor/app:ref:refs/heads/main"
    }
  }
}

The sub condition restricts which workflow runs can assume the role — repo:stackharbor/app:ref:refs/heads/main means only runs on main of stackharbor/app. This is the GH-Actions-secure pattern. Done correctly, it is competitive with CodePipeline on the credentials axis.

CodePipeline, default setup: the pipeline’s service role assumes deploy roles in target accounts. There are no long-lived credentials anywhere — the pipeline never has a secret in its environment, the cross-account assumption is the access pattern. The IAM trust chain is in CloudFormation, version-controlled, and reviewable. This is the “IAM-native” advantage: it’s not that you cannot do equivalent in GH Actions, it’s that CodePipeline forces you to do it correctly because there is no easier shortcut.

Data residency: GitHub-hosted runners run on Azure infrastructure outside Canada by default. For a Quebec health-data workload subject to PIPEDA/Loi 25 and customer contract clauses on residency, this is the showstopper. Two responses:

  • Self-hosted GitHub runners in ca-central-1 EC2 — possible, but you now operate the runners, including patching, autoscaling, and the security boundary that comes with running arbitrary workflow code on your AWS account.
  • CodePipeline — the entire execution is in your AWS account, in the region you choose. No exfiltration concern, no runner ops.

For Quebec residency engagements, the residency question often pulls deploy stages to CodePipeline regardless of where CI lives.

The fix

The pattern that holds up for most regulated SaaS:

  1. GH Actions for CI. Lint, test, build, push to ECR. Use OIDC federation, not access keys. Tag the container image with the commit SHA. The workflow ends when the image is in ECR, not when it has been deployed.

  2. CodePipeline triggered by ECR push. EventBridge rule on the ECR PutImage event, targeting the pipeline’s start-execution. The pipeline’s first action picks up the image tag from the event detail. From here, all deploy stages run inside AWS — no GitHub involvement, no external runner.

  3. Manual approval in CodePipeline, not GH Actions. GH Actions does have environments with required reviewers, but the approval lives in the GH UI and the audit trail is on GitHub. CodePipeline approvals live in the AWS account, IAM-scoped, audit-logged via CloudTrail. For a regulated workload, the AWS-side audit trail is what you produce for the next review.

  4. Deploy via CodeDeploy from CodePipeline. Blue/green to ECS or EC2, with AlarmConfiguration for automatic rollback. The blue/green primitive in CodeDeploy is hard to replicate in GH Actions without writing a lot of glue — and the glue is the thing that breaks during the incident you were trying to avoid.

  5. Single source of truth for the deploy config. Whether the pipeline is defined in CloudFormation, CDK, or Terraform, it lives in the application repo. The pipeline-definition PR goes through the same review as application code. The pipeline is infrastructure, not a one-time setup.

Three rules that hold across the CI/CD split:

CodePipeline is not GH Actions with extra steps. It is an orchestration service with a different abstraction (stages, actions, IAM service principals). Trying to replicate every GH Actions workflow as a CodePipeline stage misses the point — the right pipeline has 5-7 stages, not 30.

GH Actions is not the cheap option for serious workloads. The free tier covers public repos and small private workloads. For a serious SaaS, the GH-hosted runner minutes add up, the self-hosted runner ops add up, and the residency story may rule it out for some customers regardless. CodePipeline is also not free, but the billing is on CodeBuild minutes and S3 storage — costs that scale predictably with deploy frequency.

The migration question is rarely “all or nothing.” The split (GH Actions for CI, CodePipeline for deploy) is the move that captures most of the benefit of each tool. A migration that flips everything to CodePipeline because of a residency conversation will lose the GH Actions iteration speed that the team is used to. A migration that flips everything to GH Actions because “we use GitHub” will lose the IAM-native deploy story when the next audit asks.

The right decision is the one that matches the workload. For a public open-source project: GH Actions for everything. For a regulated Canadian SaaS: GH Actions for CI, CodePipeline for deploy. For an AWS-native team with no GitHub footprint outside source hosting: CodePipeline end-to-end. The factors that drive the decision are residency, audit, ecosystem fit, and team familiarity — in that order.


Stack Harbor builds and operates production CodePipeline workflows as part of Managed AWS onboarding and the ongoing Code Deploy Pipeline operating layer. If your release path is still SSH + a Slack message, start with an infrastructure consult.