惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

TaoSecurity Blog
TaoSecurity Blog
L
LINUX DO - 热门话题
Spread Privacy
Spread Privacy
C
Cybersecurity and Infrastructure Security Agency CISA
B
Blog RSS Feed
P
Proofpoint News Feed
AWS News Blog
AWS News Blog
GbyAI
GbyAI
D
DataBreaches.Net
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
aimingoo的专栏
aimingoo的专栏
C
CERT Recently Published Vulnerability Notes
A
About on SuperTechFans
NISL@THU
NISL@THU
Google DeepMind News
Google DeepMind News
P
Privacy International News Feed
Martin Fowler
Martin Fowler
Hacker News - Newest:
Hacker News - Newest: "LLM"
H
Help Net Security
Cisco Talos Blog
Cisco Talos Blog
T
Troy Hunt's Blog
博客园 - 三生石上(FineUI控件)
Help Net Security
Help Net Security
V2EX - 技术
V2EX - 技术
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
云风的 BLOG
云风的 BLOG
N
News and Events Feed by Topic
C
Cyber Attacks, Cyber Crime and Cyber Security
Cloudbric
Cloudbric
H
Hacker News: Front Page
T
The Blog of Author Tim Ferriss
罗磊的独立博客
MongoDB | Blog
MongoDB | Blog
P
Proofpoint News Feed
博客园_首页
C
CXSECURITY Database RSS Feed - CXSecurity.com
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Application and Cybersecurity Blog
Application and Cybersecurity Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
L
LangChain Blog
MyScale Blog
MyScale Blog
S
Security Affairs
L
Lohrmann on Cybersecurity
Recorded Future
Recorded Future
Webroot Blog
Webroot Blog
L
LINUX DO - 最新话题
腾讯CDC
Google Online Security Blog
Google Online Security Blog
Google DeepMind News
Google DeepMind News
T
Tor Project blog

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
Kargo: The Missing Piece Between CI and ArgoCD
Szymon Matus · 2026-04-24 · via DEV Community

ArgoCD deploys what's in Git. That's the whole point. It watches a repository, diffs the desired state against the cluster, and reconciles.

The question nobody answers cleanly: who updates Git?

The Problem

In a typical GitOps setup you have two repositories. The application repo where developers write code, and the GitOps repo where Kubernetes manifests live. CI builds a container image, pushes it to a registry, and then... someone or something needs to update the image tag in the GitOps repo so ArgoCD picks it up.

The common approach is to have the CI pipeline commit directly to the GitOps repo. GitLab CI finishes building your Docker image, pushes it to ECR, then runs a job that clones the GitOps repo, updates a YAML file, commits, and pushes. It works.

It also couples your application pipeline to the structure of your GitOps repository. If you reorganize your Kustomize overlays, you're updating CI pipelines in every service repo. If a team wants to change how deployments flow through environments, they're editing .gitlab-ci.yml files. The CI pipeline knows too much about things that aren't its concern.

I hit this exact scenario on a greenfield project. We had GitLab CI for builds, AWS ECR as the registry, and ArgoCD managing deployments. The CI-commits-to-GitOps approach felt too tightly coupled. I wanted something that owned the space between "artifact exists in a registry" and "manifest is updated in Git."

That's where Kargo comes.

What Kargo Does

Kargo is a continuous promotion platform built by Akuity, the company behind ArgoCD. It doesn't replace ArgoCD - it complements it. ArgoCD handles deployment (cluster state matches Git). Kargo handles promotion (Git state gets updated when new artifacts appear).

The core concepts map cleanly to what you'd build yourself if you had the time:

  • Warehouses monitor registries for new artifacts (container images, Helm charts, Git commits). When something new appears, a Warehouse packages the references into a piece of Freight.
  • Freight is a meta-artifact - a box containing references to specific versions of your images and charts. It travels through your pipeline as a unit.
  • Stages are promotion targets, roughly equivalent to environments. They form a pipeline: dev, staging, prod. Freight moves from one Stage to the next.
  • PromotionTasks define the actual steps to execute when promoting Freight to a Stage. Clone a repo, update a YAML value, commit, push.
  • Projects provide tenancy and policy. Each project gets its own namespace and RBAC.

For the full picture, the Kargo documentation covers these concepts well.

The Architecture

Here's the flow we ended up with:

Developer commits code
       |
       v
GitLab CI: test, build, push
       |
       v
Container image + Helm chart land in AWS ECR
       |
       v
Kargo Warehouse detects new artifact
       |
       v
Freight is created
       |
       v
Stage "dev" promotes automatically (or manually)
  - ClusterPromotionTask clones GitOps repo
  - Updates image tag / chart version in overlay YAML
  - Commits and pushes
       |
       v
ArgoCD detects Git change, syncs to cluster
       |
       v
Stage "prod" promotes manually from Kargo UI
  - Same ClusterPromotionTask, different overlay path
       |
       v
ArgoCD syncs prod

Enter fullscreen mode Exit fullscreen mode

The CI pipeline stops caring after the artifact hits the registry. It doesn't know about Kustomize overlays, environment structures, or ArgoCD. Kargo handles all of that.

ClusterPromotionTasks: Standardized and Reusable

One of the things I appreciated most about Kargo is the separation between ClusterPromotionTasks and PromotionTasks.

A ClusterPromotionTask is cluster-wide. You define it once, and every Kargo project can reference it. This is where you standardize your promotion logic. In our case, we have two:

Image promotion - clones the GitOps repo, updates the image tag in a specific YAML file, commits, and pushes:

apiVersion: kargo.akuity.io/v1alpha1
kind: ClusterPromotionTask
metadata:
  name: cluster-promote-image-git
spec:
  vars:
    - name: branch
      value: master
    - name: repoURL
      value: git@gitlab.com:my-org/gitops-repo.git
    - name: yamlFilename
    - name: envPath
    - name: image
  steps:
    - uses: git-clone
      config:
        repoURL: ${{ vars.repoURL }}
        checkout:
          - branch: ${{ vars.branch }}
            create: true
            path: ./out-image
    - uses: yaml-update
      as: update-image
      config:
        path: ./out-image/${{ vars.envPath }}/${{ vars.yamlFilename }}
        updates:
          - key: spec.source.helm.valuesObject.image.tag
            value: ${{ quote(imageFrom( vars.image ).Tag) }}
    - uses: git-commit
      as: commit
      config:
        path: ./out-image
        message: ${{ task.outputs['update-image'].commitMessage }}
    - uses: git-push
      config:
        path: ./out-image

Enter fullscreen mode Exit fullscreen mode

Chart promotion - same pattern, but updates the Helm chart version:

apiVersion: kargo.akuity.io/v1alpha1
kind: ClusterPromotionTask
metadata:
  name: cluster-promote-helm-git
spec:
  vars:
    - name: branch
      value: master
    - name: repoURL
      value: git@gitlab.com:my-org/gitops-repo.git
    - name: chart
    - name: envPath
    - name: yamlFilename
  steps:
    - uses: git-clone
      config:
        repoURL: ${{ vars.repoURL }}
        checkout:
          - branch: ${{ vars.branch }}
            create: true
            path: ./out-chart
    - uses: yaml-update
      as: update-chart
      config:
        path: ./out-chart/${{ vars.envPath }}/${{ vars.yamlFilename }}
        updates:
          - key: spec.source.targetRevision
            value: ${{ quote(chartFrom( vars.chart ).Version) }}
    - uses: git-commit
      as: commit
      config:
        path: ./out-chart
        message: ${{ task.outputs['update-chart'].commitMessage }}
    - uses: git-push
      config:
        path: ./out-chart

Enter fullscreen mode Exit fullscreen mode

Both tasks use variables for the environment path and filename, making them work across any application and any environment overlay. The defaults (repo URL, branch) are set once at the cluster level. Individual Stages only provide what's unique to them.

Kargo UI showing a completed promotion with step-by-step execution of two ClusterPromotionTasks

A PromotionTask (without the "Cluster" prefix) is namespaced - scoped to a single Kargo project. If a specific application needs custom promotion logic (maybe it requires a database migration step, or a Slack notification, or a different Git update strategy), you define a PromotionTask in that project's namespace. It overrides or extends the cluster-wide standard without affecting anything else.

This gives you standardized pipelines across the cluster, with per-project escape hatches when needed. Check the promotion steps reference for the full list of built-in steps.

Per-Application Pipelines

Each application gets its own Kargo Project with Warehouses, Stages, and promotion policies. Here's what a typical backend service looks like.

Project and policies - auto-promote to dev, manual gate for prod:

apiVersion: kargo.akuity.io/v1alpha1
kind: Project
metadata:
  name: my-backend
---
apiVersion: kargo.akuity.io/v1alpha1
kind: ProjectConfig
metadata:
  name: my-backend
  namespace: my-backend
spec:
  promotionPolicies:
    - stage: dev
      autoPromotionEnabled: true
    - stage: prod
      autoPromotionEnabled: false

Enter fullscreen mode Exit fullscreen mode

Warehouses - watching ECR for new images and Helm charts:

apiVersion: kargo.akuity.io/v1alpha1
kind: Warehouse
metadata:
  name: ecr-images
  namespace: my-backend
spec:
  freightCreationPolicy: Automatic
  subscriptions:
    - image:
        repoURL: 123456789.dkr.ecr.eu-south-1.amazonaws.com/my-org/backend
        allowTagsRegexes:
          - ^[a-f0-9]{8}$
        ignoreTagsRegexes:
          - ^latest$
        imageSelectionStrategy: NewestBuild
        discoveryLimit: 20
        cacheByTag: true
---
apiVersion: kargo.akuity.io/v1alpha1
kind: Warehouse
metadata:
  name: ecr-charts
  namespace: my-backend
spec:
  freightCreationPolicy: Automatic
  subscriptions:
    - chart:
        repoURL: oci://123456789.dkr.ecr.eu-south-1.amazonaws.com/my-org/backend
        discoveryLimit: 5

Enter fullscreen mode Exit fullscreen mode

In a trunk-based workflow, every merge to main produces an image tagged with the commit SHA. A single Warehouse watches for those tags and creates Freight. The same Freight then travels through the pipeline - dev first, prod after. The NewestBuild strategy picks the most recently built image, not just the lexicographically newest tag.

Stages - dev gets Freight directly from the Warehouse, prod requires Freight that passed through dev first:

kind: Stage
apiVersion: kargo.akuity.io/v1alpha1
metadata:
  name: dev
  namespace: my-backend
spec:
  requestedFreight:
    - origin:
        kind: Warehouse
        name: ecr-images
      sources:
        direct: true
    - origin:
        kind: Warehouse
        name: ecr-charts
      sources:
        direct: true
  promotionTemplate:
    spec:
      steps:
        - task:
            name: cluster-promote-image-git
            kind: ClusterPromotionTask
          vars:
            - name: image
              value: 123456789.dkr.ecr.eu-south-1.amazonaws.com/my-org/backend
            - name: envPath
              value: applications/overlays/dev/my-backend
            - name: yamlFilename
              value: patches.yaml
        - task:
            name: cluster-promote-helm-git
            kind: ClusterPromotionTask
          vars:
            - name: chart
              value: oci://123456789.dkr.ecr.eu-south-1.amazonaws.com/my-org/backend
            - name: envPath
              value: applications/overlays/dev/my-backend
            - name: yamlFilename
              value: patches.yaml
---
kind: Stage
apiVersion: kargo.akuity.io/v1alpha1
metadata:
  name: prod
  namespace: my-backend
spec:
  requestedFreight:
    - origin:
        kind: Warehouse
        name: ecr-images
      sources:
        availabilityStrategy: All
        stages:
          - dev
    - origin:
        kind: Warehouse
        name: ecr-charts
      sources:
        availabilityStrategy: All
        stages:
          - dev
  promotionTemplate:
    spec:
      steps:
        - task:
            name: cluster-promote-image-git
            kind: ClusterPromotionTask
          vars:
            - name: image
              value: 123456789.dkr.ecr.eu-south-1.amazonaws.com/my-org/backend
            - name: envPath
              value: applications/overlays/prod/my-backend
            - name: yamlFilename
              value: patches.yaml
        - task:
            name: cluster-promote-helm-git
            kind: ClusterPromotionTask
          vars:
            - name: chart
              value: oci://123456789.dkr.ecr.eu-south-1.amazonaws.com/my-org/backend
            - name: envPath
              value: applications/overlays/prod/my-backend
            - name: yamlFilename
              value: patches.yaml

Enter fullscreen mode Exit fullscreen mode

The key detail in the prod Stage: stages: [dev] under sources. Freight isn't available for promotion to prod until it has been successfully promoted to dev. This is the natural flow - the same artifact proves itself in dev before it can move forward.

Adapting to Branch-Based Workflows

Not every team uses trunk-based development, and Kargo adapts to that. If your CI produces branch-prefixed tags (e.g., dev-abc1234 from the dev branch, master-abc1234 from master), you can split the image Warehouses per branch:

apiVersion: kargo.akuity.io/v1alpha1
kind: Warehouse
metadata:
  name: ecr-images-dev
  namespace: my-backend
spec:
  subscriptions:
    - image:
        repoURL: 123456789.dkr.ecr.eu-south-1.amazonaws.com/my-org/backend
        allowTagsRegexes:
          - ^dev-.*
        imageSelectionStrategy: NewestBuild
---
apiVersion: kargo.akuity.io/v1alpha1
kind: Warehouse
metadata:
  name: ecr-images-master
  namespace: my-backend
spec:
  subscriptions:
    - image:
        repoURL: 123456789.dkr.ecr.eu-south-1.amazonaws.com/my-org/backend
        allowTagsRegexes:
          - ^master-.*
        imageSelectionStrategy: NewestBuild

Enter fullscreen mode Exit fullscreen mode

In this setup, the dev Stage pulls from ecr-images-dev and the prod Stage pulls from ecr-images-master directly, rather than requiring Freight to flow through dev first. The Warehouses, tag filters, and Stage wiring change - the ClusterPromotionTasks stay exactly the same. That's the flexibility: Kargo's promotion logic is decoupled from how your team manages branches and tagging.

Kargo UI pipeline view showing Warehouses, Freight, and Stages for a backend service

Notice how the Stages just reference the ClusterPromotionTasks and pass environment-specific variables. The promotion logic itself is defined once. Adding a new application means creating a new set of Warehouses, Stages, and a Project - not writing new promotion logic.

Kargo for Infrastructure Too

This is where Kargo surprised me. I initially set it up for application deployments, but quickly realized it works just as well for tracking infrastructure component versions.

We have Traefik deployed via Helm from the upstream chart. Keeping Helm charts up to date across environments is one of those tasks that's easy to forget. You check the release page every few weeks, maybe.

With Kargo, a Warehouse watches the upstream Helm chart repository:

apiVersion: kargo.akuity.io/v1alpha1
kind: Warehouse
metadata:
  name: ghcr-charts
  namespace: kargo-traefik
spec:
  freightCreationPolicy: Automatic
  subscriptions:
    - chart:
        repoURL: oci://ghcr.io/traefik/helm/traefik
        discoveryLimit: 10

Enter fullscreen mode Exit fullscreen mode

When a new Traefik chart version is published, Freight appears in the Kargo UI. I can see what version I'm running, what's available, and promote with a click. It's effectively a dependency dashboard that also handles the update.

Kargo UI showing available Traefik Helm chart versions ready to promote

For infrastructure components, we keep auto-promotion disabled for both dev and prod. These updates should be intentional. Kargo just makes sure you know there's something new and gives you a one-click path to deploy it.

What I Liked

Rollbacks are git commits. When you roll back in Kargo, it doesn't manipulate ArgoCD's internal state. It makes a new commit to your Git repo with the previous version. Your Git history stays the single source of truth. No hidden state, no "ArgoCD thinks it's running X but Git says Y" confusion.

Developers can self-serve. The Kargo UI is clean and intuitive for daily use. A developer can see what version is in dev, what's available, and promote to prod - all without touching Git or CI. For teams where you want to expose deployment controls without giving everyone access to ArgoCD or the GitOps repo, this is valuable.

Clean separation of concerns. CI builds artifacts and pushes them to a registry. Full stop. It doesn't know about environments, Kustomize overlays, or ArgoCD. Kargo owns the promotion logic. ArgoCD owns the deployment. Each tool does one thing.

The abstraction layer works for everyone. CI/CD pipelines just build and forget. Developers get a visual pipeline with drag-and-drop (or one-click) promotions. DevOps can define and enforce promotion policies per project. Platform teams get a standardized, reusable promotion framework. Different stakeholders interact with the same system at different levels.

The Rough Edges

Namespace proliferation. Every Kargo Project creates its own namespace. If you have ten services, you get ten Kargo namespaces on top of your existing ones. This was mostly an aesthetic annoyance - it works fine, but kubectl get ns gets busy. This might have been my own design choice rather than a Kargo requirement, but I wanted to separate Kargo resources from application workloads.

ArgoCD annotation limitation. Kargo can trigger an ArgoCD Application refresh after pushing a commit, saving you from waiting for ArgoCD's polling interval. It uses the kargo.akuity.io/authorized-stage annotation on the ArgoCD Application resource to authorize this. The problem: this annotation only supports a single Kargo project and stage. If you use the app-of-apps pattern (which is common), you'd want multiple Kargo projects to be able to refresh your root Application. That's currently not possible - no comma-delimited or glob support. We fell back to ArgoCD's default polling, which means there's a small delay between Kargo's git push and ArgoCD picking it up. Functional, not ideal.

Polling vs webhooks. Kargo Warehouses poll registries at intervals by default. If the interval is too long, you get delayed Freight creation on top of ArgoCD's polling delay. Too short, and you're hammering your registry. For AWS ECR, there's no official webhook receiver yet. A generic webhook triggered by CI after a successful push would solve this, though it slightly re-couples CI with Kargo (even if it's just a notification). The Kargo docs acknowledge this trade-off.

Active development means change. Kargo is still maturing. The product itself has been rock-solid in our experience - zero failures or downtime. The concern is more about API surface evolution and potential breaking changes between versions. The Akuity team is responsive and the project moves fast, which is both a strength and something to plan for.

Would I Use It Again?

Yes. Without hesitation for a project of similar scope - a small-to-medium team with some tolerance for adopting relatively new tooling.

Kargo filled a real gap in our GitOps workflow. The CI pipeline is simpler because it doesn't manage deployment concerns. The GitOps repo stays clean because updates come through a structured, auditable process rather than arbitrary CI commits. Developers have visibility and control over promotions without needing to understand the underlying Git structure.

For larger organizations or risk-averse environments, I'd wait until the API surface stabilizes more and the annotation limitation gets resolved. The product is stable. The ecosystem around it is still catching up.

One thing I'd do differently: set up webhook notifications from CI to Kargo from day one, rather than relying purely on polling. The extra latency from double-polling (Kargo polls registry + ArgoCD polls Git) is noticeable when you're used to instant feedback.

Kargo is built by the ArgoCD creators and feels like a natural part of the ecosystem. If you're running ArgoCD and have solved the "who updates Git?" question differently, I'd be curious how. Are you committing from CI? Using image-updater? Something custom? And if you've tried Kargo - what's been your experience with it at scale?