Setting Up a CI/CD Pipeline with GitHub Actions for a UK Fintech

A payment API that fails a test suite at 2am is an incident. A release that reaches production with nobody able to say who approved it is a regulatory problem. UK fintech teams live with both, and GitHub Actions can carry more of that weight than most people assume — provided you build the pipeline with the paperwork in mind from the start, rather than the week before an audit.

Decide what the pipeline has to prove

Before writing a line of YAML, get three questions answered and written down somewhere in the repository. Everything else follows from them.

  • Who is allowed to merge into main, and who is allowed to deploy to production?
  • What evidence must exist for a change to be explainable six months from now?
  • Which checks are permitted to block a release, and who can override them?

The answers depend on your regulatory position. FCA and PRA expectations around operational resilience, change management and segregation of duties apply broadly, and frameworks such as PCI DSS, ISO 27001 or SOC 2 add their own specifics. Your compliance lead — and, where relevant, your legal advisers — should confirm what applies to you. Your job is to make the pipeline the place where those controls actually live, so nobody has to reconstruct them from memory during a review.

Split the work into a small number of workflows

Three workflow files will cover most fintech teams.

  • ci.yml — triggered on pull_request. Lint, type-check, unit tests, build. If it takes longer than ten minutes, people will start working around it.
  • security.yml — on pull_request and on a nightly schedule. The scheduled run matters: a dependency that was clean last week can pick up a CVE without a single line of your code changing.
  • release.yml — on tag or merge to main, attached to a protected environment with an explicit approval step.

Put the shared parts into reusable workflows using workflow_call, so five repositories do not drift into five different definitions of "tested". A new service then inherits the same gate as an old one, which is a far easier argument to make to an auditor than a spreadsheet comparing job names.

Make the required checks trustworthy

Branch protection with required status checks is the backbone of the whole thing, and it has one common trap. If a job is skipped because of a path filter, the check can sit in an Expected state and block merges for the wrong reason. Run a final aggregator job with if: always() that depends on the real jobs and fails when any of them failed or was cancelled. Require that aggregator, not the individual jobs.

Then make the tests worth blocking on. Unit tests are the floor. Integration tests against a throwaway Postgres service container catch the migration mistakes that hurt most, and contract tests stop a change in one service quietly breaking another. Set a coverage threshold, but treat it as a ratchet rather than a target — raising it slowly beats arguing about it weekly. Flaky tests deserve their own rule: quarantine the test, open a ticket with an owner, fix it within the sprint. Unfixed flakiness is how teams learn to click "re-run" instead of reading the failure.

Security scanning people don't route around

Layer the scans, and give each one a clear failure policy.

  • Secret scanning with push protection enabled, so a key never lands in a branch in the first place.
  • Dependency review on pull requests, plus Dependabot alerts and a monthly triage slot.
  • Static analysis such as CodeQL on pull requests and on a schedule.
  • Container image scanning, and an SBOM generated at build time and stored as an artifact.
  • Infrastructure-as-code scanning if your environments are defined in Terraform or similar.

Fail the build on high and critical findings that are reachable in your code. Medium findings should become tickets with an owner and a date, not build failures. A pipeline that blocks on everything gets bypassed within a fortnight, and a bypassed control is worse than no control, because it looks like one.

Secrets, OIDC and production approval

Static cloud credentials stored as repository secrets are the habit worth breaking first. Use OpenID Connect to let the workflow assume a cloud role for the duration of the job. Nothing long-lived to leak, rotate or explain.

For production, use an environment with required reviewers and restrict the list of approvers to people whose role justifies it. GitHub records who approved each deployment and when — that approval entry is often the single most useful piece of evidence you will produce. Keep secrets in environment scope rather than repository scope, mask anything that might be echoed, and keep a written rotation schedule for whatever genuinely cannot be short-lived.

Audit logs and evidence worth keeping

Stream the organisation audit log to your SIEM — S3, Splunk, Event Hubs and similar destinations are all supported. Merges, approval decisions, permission changes and workflow edits then land in the same place as the rest of your security telemetry, with retention you control.

In the repository, enable required reviews via CODEOWNERS, consider signed commits, and prevent force pushes to protected branches. Artifacts expire by default after a set retention period, so raise it deliberately and copy release evidence — test reports, scan results, the SBOM — into storage you own, ideally with object lock enabled. Tie each run back to a change record: a ticket reference in the branch name or pull request description gives you the "why" to sit alongside the pipeline's "how".

A sensible order to roll it out

  1. Branch protection, required reviews, CODEOWNERS, no force pushes.
  2. CI on pull requests with fast, reliable tests and the aggregator job.
  3. Secret scanning and dependency review — the cheapest wins available.
  4. OIDC for deployments, plus a protected production environment with named approvers.
  5. Static analysis and image scanning, tuned until the noise is manageable.
  6. Audit log streaming and long-term evidence storage.
  7. Reusable workflows, so every new repository starts compliant by default.

Keep the gate boring

Once the pipeline works, resist the urge to add fifteen more jobs. Record why each gate exists in a short decision note next to the workflow, review the thresholds quarterly, and rehearse the emergency path so a genuine incident does not become the first time anyone tries to bypass an approval step. If a control is unclear, or the answer depends on how your firm is authorised, ask your compliance team or legal advisers rather than guessing. The goal is a pipeline nobody thinks about, quietly producing the evidence you would otherwise spend a fortnight assembling.

Photo: Mikhail Nilov / Pexels

Related News
New Year Codebase Health Check: A January Checklist for Development Teams

A practical January checklist for development teams: audit dependencies, target test coverage, prune...

Why Your CSS Grid Layout Breaks on Mobile: Common Mistakes and Fixes

CSS Grid usually breaks on mobile because of sizing floors, not the grid itself. Here's why implicit...

A Developer's Checklist for GDPR-Compliant Logging

A practical checklist for keeping personal data out of application logs, setting sensible retention...