GitOps vs GitHub Actions: Security-First in Production

GitOps vs GitHub Actions: Security-First in Production - Photo by Godfrey Nyangechi on Unsplash

Last month I migrated two production clusters from GitHub Actions-only deployments to a hybrid GitOps setup with ArgoCD. The trigger? A misconfigured workflow secret that exposed an AWS key for 11 minutes before our scanner caught it. Nothing happened — this time. But it made me rethink how we handle the boundary between CI and CD.

Here’s what I learned about running both tools securely in production, and when each one actually makes sense.

GitOps: Let Git Be the Only Way In

GitOps treats Git as the single source of truth for your cluster state. You define what should exist in a repo, and an agent like ArgoCD or Flux continuously reconciles reality to match. No one SSHs into production. No one runs kubectl apply by hand.

The security model here is simple: the cluster pulls config from Git. The agent runs inside the cluster with the minimum permissions needed to apply manifests. Your developers never need direct cluster access — they open a PR, it gets reviewed, merged, and the agent picks it up.

This is a massive reduction in attack surface. In a traditional CI/CD model, your pipeline needs credentials to push to the cluster. With GitOps, those credentials stay inside the cluster.

Here’s a basic ArgoCD Application manifest:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: my-app
spec:
  source:
    repoURL: https://github.com/my-org/my-app-config
    targetRevision: HEAD
    path: .
  destination:
    server: https://kubernetes.default.svc
    namespace: my-app-namespace
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

The selfHeal: true setting is important — if someone does manage to modify a resource directly in the cluster, ArgoCD will revert it to match Git. That’s drift detection for free.

One gotcha: make sure you enforce branch protection on your GitOps repos. I’ve seen teams set up ArgoCD perfectly, then leave the main branch unprotected. Anyone with repo write access can then deploy anything. Always require reviews and status checks.

GitHub Actions: Powerful but Exposed

GitHub Actions is a different animal. It’s event-driven — push code, open a PR, hit a schedule, and workflows fire. That flexibility is exactly what makes it harder to secure.

Every GitHub Actions workflow that deploys to production needs some form of credential. Even with OIDC federation (which you should absolutely be using — see my guide on securing GitHub Actions with OIDC), there are still risks. Third-party actions can be compromised. Workflow files can be modified in feature branches. Secrets can leak through step outputs if you’re not careful.

Here’s a typical deployment workflow:

name: Deploy to Kubernetes
on:
  push:
    branches:
      - main
jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      - name: Configure kubectl
        uses: azure/setup-kubectl@v3
      - name: Deploy application
        run: kubectl apply -f k8s/deployment.yaml

Notice the environment: production — that enables environment protection rules, so deployments require manual approval. Without it, any push to main goes straight to prod. I always set this up, even on small projects.

The bigger issue is that GitHub Actions workflows are imperative. You’re writing step-by-step instructions that execute on a runner with network access. Compare that to GitOps where you declare “this is what should exist” and an agent figures out the rest. The imperative model has more moving parts, and more places for things to go wrong.

Where Each One Wins on Security

After running both in production, here’s how I’d break it down:

Access control — GitOps wins. The agent pulls from Git, so your CI system never needs cluster credentials. With GitHub Actions, your workflow needs some path to the cluster, whether that’s a kubeconfig, OIDC token, or service account. That’s another secret to manage.

Secret handling — GitOps is cleaner. You pair it with something like External Secrets Operator or Sealed Secrets and your Git repo never contains actual credentials. GitHub Actions has encrypted secrets, but they’re injected into the runner environment at build time — a compromise of the runner means a compromise of those secrets.

Audit trail — GitOps. Every change is a Git commit with an author, timestamp, and review trail. GitHub Actions logs exist, but they expire and they’re harder to query when you need to answer “who deployed what, and when?” during an incident.

Flexibility — GitHub Actions. Not everything fits the GitOps model. Running test suites, building container images, scanning for vulnerabilities, sending notifications — these are CI tasks, and GitHub Actions handles them well. Trying to force these into a GitOps workflow is pain.

Speed of setup — GitHub Actions. You can go from zero to deployed in an afternoon. GitOps requires more upfront investment: installing the agent, structuring your config repos, setting up GitOps security patterns.

The Hybrid Approach (What Actually Works)

Most teams I’ve worked with end up running both, and honestly it’s the right call. Use GitHub Actions for CI — build, test, scan, push images. Use GitOps for CD — let ArgoCD or Flux handle what’s running in the cluster.

The boundary is important: GitHub Actions should never directly kubectl apply to production. Instead, it updates the image tag in your GitOps repo (via a PR or direct commit to a deploy branch), and the GitOps agent picks it up.

This gives you:

  • Full Git audit trail for all production changes
  • No cluster credentials in your CI system
  • Automatic drift detection and self-healing
  • The flexibility of GitHub Actions for everything that isn’t deployment

One thing to watch: make sure your GitHub Actions workflow doesn’t have permissions to modify the GitOps repo directly without review. Use a bot account with limited scope, and still require PR approval for production changes.

Adding Security Scanning to the Pipeline

Whether you use GitOps, GitHub Actions, or both, you need automated security checks. I run Trivy on every image build and OPA/Gatekeeper for policy enforcement in the cluster.

Here’s how I integrate Trivy into a GitHub Actions workflow:

name: Security Scan
on:
  pull_request:
jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Build image
        run: docker build -t my-app:${{ github.sha }} .
      - name: Trivy scan
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: my-app:${{ github.sha }}
          severity: CRITICAL,HIGH
          exit-code: 1

The exit-code: 1 means the workflow fails if critical or high vulnerabilities are found. No exceptions. I’ve had developers complain about this blocking their PRs, but it’s caught real issues — including a supply chain problem in a base image that would have made it to prod otherwise.

What I’d Do Starting Fresh

If I were setting up a new production Kubernetes environment today:

  1. ArgoCD for all cluster deployments, with strict branch protection and required reviews on the config repo
  2. GitHub Actions for CI only — build, test, scan, push to registry
  3. External Secrets Operator for credentials, never stored in Git
  4. OPA Gatekeeper for policy enforcement (no privileged containers, required resource limits, etc.)
  5. Trivy in CI, plus periodic scanning of running images

The investment in GitOps pays off fast once you’re past the initial setup. The first time you need to answer “what changed?” during a 2 AM incident and the answer is right there in the Git log, you’ll be glad you did it.

🛠️ Recommended Resources:

Get daily AI-powered market intelligence. Join Alpha Signal — free market briefs, security alerts, and dev tool recommendations.
📋 Disclosure: Some links in this article are affiliate links. If you purchase through these links, I earn a small commission at no extra cost to you. I only recommend products I’ve personally used or thoroughly evaluated. This helps support orthogonal.info and keeps the content free.

📧 Get weekly insights on security, trading, and tech. No spam, unsubscribe anytime.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

Also by us: StartCaaS — AI Company OS · Hype2You — AI Tech Trends