Tag: GitOps best practices

  • GitOps vs GitHub Actions: Security-First in Production

    GitOps vs GitHub Actions: Security-First in Production

    Migrating from GitHub Actions-only deployments to a hybrid GitOps setup with ArgoCD changes your security posture fundamentally—but the tradeoffs aren’t obvious until you’ve lived with both in production. The shift affects secret management, drift detection, and rollback speed in ways the docs undersell.

    Quick Answer: For security-critical production environments, GitOps (ArgoCD/Flux) is the better choice over GitHub Actions because it enforces declarative state, provides drift detection, and keeps credentials out of CI pipelines. Use GitHub Actions for building/testing, and GitOps for deploying.

    TL;DR: GitOps (ArgoCD/Flux) and GitHub Actions serve different roles in production. GitHub Actions excels at CI — building, testing, scanning. GitOps excels at CD — declarative deployments with drift detection and automatic rollback. The security-first approach: use GitHub Actions for CI, GitOps for CD, and never store deployment credentials in CI pipelines. This hybrid model reduces secret exposure and gives you audit-grade deployment history.

    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.

    FAQ

    Can I use GitHub Actions and ArgoCD together?

    Yes, and this is the recommended production pattern. GitHub Actions handles CI (build, test, scan, push images), then updates a GitOps manifest repo. ArgoCD watches that repo and handles the actual deployment. This separation means your CI system never needs cluster credentials.

    Is GitOps more secure than traditional CI/CD?

    Generally yes. GitOps eliminates the need to store cluster credentials in CI pipelines — the biggest source of credential leaks. ArgoCD pulls from Git (no inbound access needed), provides drift detection, and creates an immutable audit trail of every deployment. The tradeoff is added complexity in the initial setup.

    What about Flux vs ArgoCD?

    Flux is lighter, more composable, and integrates tightly with the Kubernetes API. ArgoCD has a better UI, supports multi-cluster out of the box, and has a larger ecosystem. For security-focused teams, both are excellent — Flux edges ahead for GitOps-native workflows, ArgoCD for teams that want visual deployment management.

    References

  • Self-Hosted GitOps Pipeline: Gitea + ArgoCD Guide

    Self-Hosted GitOps Pipeline: Gitea + ArgoCD Guide

    I self-host Gitea on my TrueNAS homelab and use it to deploy everything from trading bots to media servers. The error message that started this guide was maddening: “Permission denied while cloning repository.” It was my repository. On my server. In my basement. Yet somehow, my GitOps pipeline decided to stage a mutiny. If you’ve ever felt personally attacked by your own self-hosted CI/CD setup, you’re not alone.

    This article is here to save your sanity (and maybe your cat’s life). We’re diving deep into building a self-hosted GitOps pipeline using Gitea, ArgoCD, and Kubernetes on your home lab. Whether you’re a homelab enthusiast or a DevOps engineer tired of fighting with cloud services, this guide will help you take back control. No more cryptic errors, no more dependency nightmares—just a clean, reliable pipeline that works exactly how you want it to. Let’s roll up our sleeves and fix this mess.


    What is GitOps and Why Self-Host?

    🔧 From my experience: The biggest win of self-hosted GitOps isn’t automation—it’s auditability. Every change to my infrastructure is a git commit with a timestamp and a diff. When something breaks at 2 AM, I run git log --oneline -5 and immediately see what changed. That alone has saved me hours of debugging.

    📌 TL;DR: The error message was maddening: “Permission denied while cloning repository.” It was my repository. I own everything here, including the questionable Wi-Fi router and the cat that keeps unplugging cables. Yet somehow, my GitOps pipeline decided to stage a mutiny.
    🎯 Quick Answer: A self-hosted GitOps pipeline using Gitea as the Git server and ArgoCD for continuous deployment provides full CI/CD control on homelab or TrueNAS hardware without relying on GitHub or cloud services. Gitea handles repositories and webhooks while ArgoCD syncs cluster state from Git.

    GitOps is a big improvement for managing infrastructure and application deployments. At its core, GitOps means using Git as the single source of truth for your system’s desired state. Instead of manually tweaking configurations or relying on someone’s “I swear this works” bash script, GitOps lets you define everything declaratively in Git repositories. Kubernetes then syncs your cluster to match the state defined in Git. It’s automated, repeatable, and—when done right—beautifully simple.

    But why self-host your CI/CD pipeline? For homelab enthusiasts, self-hosting is the ultimate flex. It’s like growing your own vegetables instead of buying them at the store. You get full control, no vendor lock-in, and the satisfaction of knowing you’re running everything on your own hardware. For DevOps engineers, self-hosting means tailoring the pipeline to your exact needs, ensuring workflows are as efficient—or chaotic—as you want them to be.

    💡 Pro Tip: Start small with a single project before going full GitOps on your entire homelab. Debugging a broken pipeline at 2 AM is not fun.

    Key Tools for Your Pipeline

    • Gitea: A lightweight, self-hosted Git service. Think of it as GitHub’s chill cousin who doesn’t charge you for private repos.
    • ArgoCD: The GitOps powerhouse that syncs your Git repositories with your Kubernetes clusters. It’s like having a personal assistant for your deployments.
    • Kubernetes: The container orchestration king. If you’re not using Kubernetes yet, prepare for a rabbit hole of YAML files and endless possibilities.
    🔐 Security Note: Self-hosting means you’re responsible for securing your pipeline. Always use HTTPS, configure firewalls, and limit access to your repositories.

    Step 1: Setting Up Your Home Kubernetes Cluster

    Setting up a Kubernetes cluster at home is both thrilling and maddening. Think of it like assembling IKEA furniture, but instead of a bookshelf, you’re building a self-hosted CI/CD powerhouse. Let’s break it down.

    Hardware Requirements

    You don’t need a data center in your basement (though if you have one, I’m jealous). A few low-power devices like Raspberry Pis or Intel NUCs will do the trick. Here’s what you’ll need:

    • Raspberry Pi: Affordable and power-efficient. Go for the 4GB or 8GB models.
    • Intel NUC: More powerful than a Pi, great for running heavier workloads like Gitea or ArgoCD.
    • Storage: Use SSDs for speed. Slow storage will bottleneck your CI/CD jobs.
    • Networking: A decent router or switch is essential. VLAN support is a bonus for network segmentation.
    💡 Pro Tip: If you’re using Raspberry Pis, invest in a reliable USB-C power supply. Flaky power leads to flaky clusters.

    Installing Kubernetes with k3s

    For simplicity, we’ll use k3s, a lightweight Kubernetes distribution perfect for home labs. Here’s how to get started:

    
    # Download the k3s installation script
    curl -sfL https://get.k3s.io -o install-k3s.sh
    
    # Verify the script's integrity (check the official k3s site for checksum details)
    sha256sum install-k3s.sh
    
    # Run the script manually after verification
    sudo sh install-k3s.sh
    
    # Check if k3s is running
    sudo kubectl get nodes
    
    # Join worker nodes to the cluster
    curl -sfL https://get.k3s.io -o install-k3s-worker.sh
    sha256sum install-k3s-worker.sh
    sudo sh install-k3s-worker.sh K3S_URL=https://<MASTER_IP>:6443 K3S_TOKEN=<TOKEN>
    

    Replace <MASTER_IP> and <TOKEN> with the actual values from your master node. The token can be found in /var/lib/rancher/k3s/server/node-token on the master.

    🔐 Security Note: Avoid exposing your Kubernetes API to the internet. Use a VPN or SSH tunnel for remote access.

    Optimizing Kubernetes for Minimal Infrastructure

    Running Kubernetes on a shoestring budget? Here are some tips:

    • Use GitOps: Tools like ArgoCD automate deployments and keep your cluster configuration in sync with Git.
    • Self-host Gitea: Gitea is lightweight and perfect for managing your CI/CD pipelines without hogging resources.
    • Resource Limits: Set CPU and memory limits for your pods to prevent one rogue app from taking down your cluster.
    • Node Affinity: Use node affinity rules to run critical workloads on your most reliable hardware.
    💡 Pro Tip: If you’re running out of resources, consider offloading non-critical workloads to a cloud provider. Hybrid clusters are a thing!

    Step 2: Deploying Gitea for Self-Hosted Git Repositories

    Gitea is a lightweight, self-hosted Git service that’s perfect for homelabs and serious DevOps workflows. Here’s how to deploy it:

    Deploying Gitea with Helm

    
    # Add the Gitea Helm repo
    helm repo add gitea-charts https://dl.gitea.io/charts/
    
    # Install Gitea with default values
    helm install my-gitea gitea-charts/gitea
    

    Once deployed, configure Gitea for secure repository management:

    • Enable HTTPS: Use a reverse proxy like Nginx or Traefik for SSL termination.
    • Set User Permissions: Carefully configure access to prevent accidental force-pushes to main.
    • Use Webhooks: Integrate Gitea with ArgoCD or other automation tools for smooth CI/CD workflows.
    💡 Pro Tip: Use Gitea’s built-in API for automation. It’s like having a personal assistant for your repositories.

    Step 3: Integrating ArgoCD for GitOps

    ArgoCD is the glue that binds your Git repositories to your Kubernetes cluster. Here’s how to set it up:

    
    # Add the ArgoCD Helm repo
    helm repo add argo https://argoproj.github.io/argo-helm
    
    # Install ArgoCD
    helm install my-argocd argo/argo-cd
    

    Once installed, configure ArgoCD to sync your repositories with your cluster:

    • Define Applications: Use ArgoCD manifests to specify which repositories and branches to sync.
    • Automate Sync: Enable auto-sync to keep your cluster up-to-date with Git.
    • Monitor Health: Use ArgoCD’s dashboard to monitor application health and sync status.
    ⚠️ Gotcha: ArgoCD’s default settings may not be secure for production. Always review and harden configurations.

    Conclusion

    Building a self-hosted GitOps pipeline with Gitea, ArgoCD, and Kubernetes is one of the most rewarding homelab projects I’ve done. Once it clicks, you’ll never want to deploy manually again. Here’s what we covered:

    • GitOps simplifies infrastructure management by using Git as the single source of truth.
    • Self-hosting gives you full control over your CI/CD workflows.
    • Gitea is lightweight, customizable, and perfect for homelabs.
    • ArgoCD automates deployments and keeps your cluster in sync with Git.
    • Securing your pipeline is critical—always use HTTPS, firewalls, and access controls.

    Ready to take the plunge? Share your experience or ask questions at [email protected] Let’s build something amazing together!

    Related Reading

    If you are building out your GitOps practice, these related guides will help you level up:

    📊 Free AI Market Intelligence

    Join Alpha Signal — AI-powered market research delivered daily. Narrative detection, geopolitical risk scoring, sector rotation analysis.

    Join Free on Telegram →

    Pro with stock conviction scores: $5/mo

    Get Weekly Security & DevOps Insights

    Join 500+ engineers getting actionable tutorials on Kubernetes security, homelab builds, and trading automation. No spam, unsubscribe anytime.

    Subscribe Free →

    Delivered every Tuesday. Read by engineers at Google, AWS, and startups.

    Frequently Asked Questions

    What is Self-Hosted GitOps Pipeline: Gitea + ArgoCD Guide about?

    The error message was maddening: “Permission denied while cloning repository.” It was my repository. I own everything here, including the questionable Wi-Fi router and the cat that keeps unplugging ca

    Who should read this article about Self-Hosted GitOps Pipeline: Gitea + ArgoCD Guide?

    Anyone interested in learning about Self-Hosted GitOps Pipeline: Gitea + ArgoCD Guide and related topics will find this article useful.

    What are the key takeaways from Self-Hosted GitOps Pipeline: Gitea + ArgoCD Guide?

    Yet somehow, my GitOps pipeline decided to stage a mutiny. If you’ve ever felt personally attacked by your own self-hosted CI/CD setup, you’re not alone. This article is here to save your sanity (and

    References

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