Skip to main content
DevOps9 min read

When GitOps at Scale Outgrows Argo CD: What LinkedIn’s Move Means for Platform Teams

LinkedIn’s replacement of Argo CD with a custom GitOps engine for Kubernetes deployments is a useful reminder that even successful tools can become constraints at scale. For engineering leaders, the lesson is not to rebuild everything, but to know when deployment infrastructure has become strategic enough to warrant deeper ownership.

GitOps often starts as a simplification: put desired state in Git, let automation reconcile it, and make deployments more auditable. But at large scale, the same default toolchain that once reduced operational complexity can become the system teams have to work around.

LinkedIn recently revealed that it replaced Argo CD with a custom GitOps engine for Kubernetes deployments, as reported by InfoQ in its coverage of the move. That decision is worth studying not because every organization should follow it, but because it highlights a modernization question many engineering leaders eventually face: when does a widely adopted platform stop fitting your operational needs?

Context: Argo CD is not the problem

When GitOps at Scale Outgrows Argo CD: What LinkedIn’s Move Means for Platform Teams
When GitOps at Scale Outgrows Argo CD: What LinkedIn’s Move Means for Platform Teams

Argo CD has become one of the most common choices for Kubernetes GitOps. It is open source, Kubernetes-native, widely understood by platform engineers, and supported by a broad ecosystem. For many teams, it remains an excellent default.

That is why LinkedIn’s decision is interesting. Replacing Argo CD does not imply Argo CD failed as a product. Instead, it suggests LinkedIn reached a scale, operating model, or integration boundary where a general-purpose tool no longer matched its internal deployment requirements closely enough.

This is a familiar pattern in infrastructure modernization. Teams adopt a standard platform to move faster, reduce variance, and avoid bespoke engineering. Over time, the platform becomes deeply embedded in release workflows, compliance controls, incident response, and developer experience. Eventually, the organization may discover that the tool is no longer just tooling. It is part of the production delivery system.

At that point, the evaluation changes from feature comparison to operational fit.

What LinkedIn’s move signals

The InfoQ report states that LinkedIn replaced Argo CD with a custom GitOps engine used for Kubernetes deployments. Even without treating LinkedIn’s architecture as a blueprint, the move points to several pressures that commonly appear in mature platform organizations.

Scale changes the economics of defaults

Default toolchains are designed for broad applicability. That is their strength. But broad applicability also means they must support many use cases, many deployment models, and many organizational patterns.

At smaller scale, flexibility is helpful. At very large scale, flexibility can introduce cost. Every abstraction has operational consequences: reconciliation behavior, controller performance, repository layout assumptions, access control models, drift detection, rollback workflows, observability, and integration with internal systems.

A platform team managing hundreds or thousands of services may need highly specific behaviors. They may need deployment orchestration that aligns with internal release policies, multi-tenant safety boundaries, custom approval flows, or fleet-wide visibility that is difficult to achieve through configuration alone.

In that context, the question becomes less about whether the default tool can be made to work and more about whether making it work creates a growing maintenance burden.

GitOps becomes part of the control plane

GitOps is sometimes described as a deployment pattern, but at scale it behaves more like a control plane. It determines how desired state is represented, who can change it, how changes are promoted, what happens during drift, and how failures are surfaced.

That means GitOps tooling sits at the intersection of developer productivity, security, reliability, and compliance. If the system is slow, opaque, or hard to extend, those costs show up across the engineering organization.

For CTOs and platform leaders, this is the key modernization insight: deployment infrastructure is not neutral. It shapes how teams ship software. A mismatch between tooling and operating model can create hidden friction that accumulates release after release.

The build-versus-buy question is really a maintenance question

LinkedIn’s custom engine naturally raises the question: should other teams build their own GitOps platform?

Usually, no. Most organizations are better served by adopting and improving standard tools, especially when they lack the scale or platform engineering capacity to maintain a custom deployment system safely. A custom GitOps engine is not a one-time project. It requires ongoing ownership, compatibility work, incident response, documentation, security review, and developer support.

But the opposite mistake is also common: treating build-versus-buy as a permanent decision. A tool that was the right choice three years ago may still be valuable, or it may have become a constraint. Modernization requires periodic reevaluation.

A useful way to frame the decision is to ask whether the current tool is still reducing total system complexity. If teams are building layers of scripts, policy workarounds, sidecars, manual exception processes, and dashboards around the tool, the organization may already be paying for a custom platform without receiving the benefits of intentional design.

This connects to a broader point raised in another recent InfoQ discussion on technical debt as the price of learning by doing. Tooling debt is often not a sign of bad decisions. It is evidence that the organization learned more about its real requirements after operating the system in production.

Signs your GitOps platform may be outgrowing its original design

Engineering leaders do not need LinkedIn’s scale to learn from the pattern. The following signals can help teams identify when GitOps tooling is becoming a modernization candidate.

1. Operational workarounds are becoming normal

If deployment success depends on tribal knowledge, manual sequencing, or unofficial scripts, the platform is no longer providing a clean abstraction. Workarounds are sometimes acceptable, but when they become standard operating procedure, they represent technical debt.

Track how often teams bypass the intended workflow. Also track how many deployment incidents involve gaps between the official process and the real process.

2. Performance issues affect release confidence

GitOps systems must reconcile desired and actual state reliably. If reconciliation lag, controller load, repository size, or event volume creates uncertainty, developers may stop trusting the platform.

The issue is not just speed. It is predictability. A slow deployment can be planned around. An unpredictable deployment system creates operational risk.

3. Internal policy does not map cleanly to the tool

As organizations mature, deployment policy often becomes more nuanced. Teams may need different controls by environment, service criticality, data classification, or compliance boundary.

If the platform cannot express those policies clearly, teams may encode them in scattered scripts or approval processes. That fragmentation makes auditing harder and increases the cost of change.

4. Developer experience is diverging across teams

One promise of platform engineering is consistency. If each team has to learn a different set of deployment conventions, the platform is not scaling as an internal product.

GitOps modernization should consider the developer interface, not only the backend engine. A technically powerful system that developers cannot understand will still generate support load and adoption problems.

5. Upgrades feel too risky to perform regularly

This is one of the clearest maintenance signals. If the team avoids upgrading deployment tooling because the blast radius is unclear, the organization has created a critical dependency it cannot comfortably change.

That does not automatically mean replacement is required. It does mean the system needs an upgrade strategy, test coverage, migration planning, and clearer ownership.

Practical implications for engineering teams

LinkedIn’s move should encourage platform teams to evaluate their deployment systems with the same rigor they apply to customer-facing services.

Treat deployment infrastructure as a product

Define users, service-level expectations, support channels, documentation standards, and a roadmap. GitOps tooling is not just an internal utility. It is the path to production for engineering teams.

Product thinking helps teams avoid overfitting to one group’s needs while still making deliberate tradeoffs.

Measure friction before proposing replacement

Before considering a custom engine, gather evidence. Useful metrics include deployment frequency, failed deployment rate, mean time to rollback, reconciliation latency, support tickets, manual intervention frequency, and time spent maintaining workarounds.

Qualitative feedback matters too. Ask developers where they lose confidence in the deployment process. Ask SREs which failure modes are hardest to diagnose. Ask security teams where policy enforcement is inconsistent.

Prefer extension before replacement

A full replacement is the highest-risk modernization path. Many issues can be addressed through better repository structure, policy engines, improved observability, controller tuning, clearer tenancy models, or platform wrappers.

The goal is not to preserve the current tool at all costs. The goal is to avoid rebuilding core infrastructure when targeted modernization would solve the actual problem.

Make ownership explicit

If a team chooses to build or heavily customize deployment infrastructure, ownership must be long term. Who handles upgrades? Who responds to incidents? Who maintains documentation? Who validates Kubernetes compatibility? Who reviews security-sensitive changes?

Custom tooling without durable ownership becomes technical debt quickly.

Plan migration as a risk-reduction program

Replacing deployment infrastructure touches production delivery. Migration should be incremental, observable, and reversible where possible.

Start with low-risk workloads, run old and new systems in parallel when practical, define rollback criteria, and publish clear migration guides. Most importantly, treat the migration itself as a modernization project with stakeholder communication, not as a background refactor.

Where Vibgrate fits into the modernization conversation

At Vibgrate, we see this pattern across software maintenance and modernization efforts: the hardest systems to replace are often the ones that were originally adopted to simplify delivery. CI/CD platforms, deployment controllers, test frameworks, and internal developer portals can all become deeply coupled to how an organization ships.

The lesson is not that teams should avoid popular tools. It is that every platform decision should include a lifecycle plan. How will the tool be upgraded? How will fit be reassessed? What signals indicate it is time to extend, consolidate, or replace it?

Modernization is healthiest when it is continuous. Waiting until the deployment platform becomes an urgent constraint usually makes the eventual migration more expensive.

Conclusion: the best platform is the one you can evolve

LinkedIn’s replacement of Argo CD with a custom GitOps engine for Kubernetes deployments is a useful case study in platform maturity. It shows that even proven tools can reach a point where organizational scale, operational requirements, or integration needs justify a different path.

For most teams, the immediate takeaway is not to build a custom GitOps engine. It is to examine whether deployment tooling is still serving the organization or quietly accumulating debt. The future of DevOps modernization will belong to teams that treat delivery infrastructure as an evolving product: measured, maintained, and periodically redesigned when reality outgrows the default.

Vibgrate CLI

See a real scan run

A replay of the actual CLI running against our test repositories — live progress, real findings, a genuine DriftScore. Nothing executes in your browser.

Replay
demo@vibgrate — bash
❯ npx @vibgrate/cli scan
 
╭──────────────────────────────────────────╮
│ Vibgrate Drift Report │
╰──────────────────────────────────────────╯
 
── node-turborepo (node) .
Runtime: >=18.0.0 (8 majors behind)
Frameworks:
Turbo: 1.13.4 → 2.11.11 (1 behind)
TypeScript: 5.9.3 → 7.0.2 (2 behind)
Dependencies:
1 current 1 1-behind 3 2+ behind 1 unknown
 
── @repo/admin (node) apps/admin
Frameworks:
TanStack Query: 5.107.1 → 5.107.1 (current)
React: 18.3.1 → 19.3.1 (1 behind)
React DOM: 18.3.1 → 19.3.1 (1 behind)
TypeScript: 5.9.3 → 7.0.2 (2 behind)
Vite: 5.4.21 → 8.3.8 (3 behind)
Dependencies:
3 current 9 1-behind 3 2+ behind 4 unknown
 
── @repo/api (node) apps/api
Frameworks:
Express: 4.22.3 → 5.3.0 (1 behind)
TypeScript: 5.9.3 → 7.0.2 (2 behind)
Vitest: 1.6.1 → 5.1.0 (4 behind)
Dependencies:
7 current 4 1-behind 4 2+ behind 4 unknown
 
── @repo/web (node) apps/web
Frameworks:
Next.js: 14.2.35 → 16.4.6 (2 behind)
React: 18.3.1 → 19.3.1 (1 behind)
React DOM: 18.3.1 → 19.3.1 (1 behind)
TypeScript: 5.9.3 → 7.0.2 (2 behind)
Dependencies:
2 current 6 1-behind 3 2+ behind 5 unknown
 
── @repo/config (node) packages/config
Frameworks:
TypeScript: 5.9.3 → 7.0.2 (2 behind)
Dependencies:
2 current 2 1-behind 5 2+ behind 0 unknown
 
── @repo/database (node) packages/database
Frameworks:
Prisma: 5.22.0 → 7.10.1 (2 behind)
TypeScript: 5.9.3 → 7.0.2 (2 behind)
Dependencies:
1 current 0 1-behind 3 2+ behind 1 unknown
 
── @repo/types (node) packages/types
Frameworks:
TypeScript: 5.9.3 → 7.0.2 (2 behind)
Dependencies:
0 current 0 1-behind 1 2+ behind 1 unknown
 
── @repo/ui (node) packages/ui
Frameworks:
React: 18.3.1 → 19.3.1 (1 behind)
TypeScript: 5.9.3 → 7.0.2 (2 behind)
React: 18.3.1 → 19.3.1 (1 behind)
Dependencies:
1 current 4 1-behind 1 2+ behind 1 unknown
 
── @repo/utils (node) packages/utils
Frameworks:
TypeScript: 5.9.3 → 7.0.2 (2 behind)
Vitest: 1.6.1 → 5.1.0 (4 behind)
Dependencies:
0 current 1 1-behind 2 2+ behind 1 unknown
 
Tech Stack
Frontend: React, React DOM
Meta-frameworks: Next.js
Bundlers: tsx, Turbo, Vite
CSS / UI: Autoprefixer, PostCSS, Tailwind CSS
Backend: Express
ORM / Database: Prisma, Prisma Client
Testing: Vitest
Lint & Format: ESLint, ESLint Prettier, ESLint React, Prettier, typescript-eslint
 
Services & Integrations
Auth: JWT 9.0.3
Databases: Prisma 5.22.0
 
TypeScript
v5.3.3 · strict ✔ · MIXED · target: ES2022
 
Build & Deploy
Package Managers: pnpm
Monorepo: npm-workspaces, pnpm-workspaces, turbo
 
Product Purpose Signals
Frameworks: react, nextjs
Evidence: 177
Top Signals:
- [heading] Dashboard (apps/admin/src/pages/Dashboard.tsx)
- [title] Revenue Overview (apps/admin/src/pages/Dashboard.tsx)
- [copy] workspace:* (packages/ui/package.json)
- [copy] ./dist (packages/ui/tsconfig.json)
- [copy] ./src/index.ts (packages/ui/package.json)
- [copy] @repo/config/tsconfig-base.json (packages/ui/tsconfig.json)
- [copy] @repo/ui (packages/ui/package.json)
- [copy] #3b82f6 (apps/admin/src/pages/Dashboard.tsx)
Unknowns:
- No pricing or billing evidence found.
- No integrations/connectors evidence found.
- No route structure evidence found.
 
Security Posture
Lockfile ✖ · .env ✔ · node_modules ✔
 
Platform
Native modules: turbo
 
Code Quality
Files: 36 · Functions: 183 · Avg complexity: 2.62 · Avg length: 21.13 lines
Max nesting: 2 · Circular deps: 0 · Dead code: 0%
God files: apps/admin/src/pages/Products (448 lines)
 
Database Schema
postgresql · 8 models · 1 enum
Models: Address, CartItem, Category, Order, OrderItem (+3 more)
 
Findings (16 errors, 11 warnings)
✖ @types/node is 6 major versions behind (spec: ^20.11.0, latest: 26.6.6).
vibgrate/dependency-major-lag in .
✖ 60% of dependencies are 2+ major versions behind in node-turborepo.
vibgrate/dependency-rot in .
✖ Node.js runtime ">=18.0.0" reached end-of-life on 2025-04-30 (latest: 26.0.0).
vibgrate/runtime-eol in .
✖ vite is 3 major versions behind (spec: ^5.0.12, latest: 8.3.8).
vibgrate/dependency-major-lag in apps/admin
✖ Vite is 3 major versions behind (current: 5.4.21, latest: 8.3.8).
vibgrate/framework-major-lag in apps/admin
✖ @types/node is 6 major versions behind (spec: ^20.11.0, latest: 26.6.6).
vibgrate/dependency-major-lag in apps/api
✖ vitest is 4 major versions behind (spec: ^1.2.1, latest: 5.1.0).
vibgrate/dependency-major-lag in apps/api
✖ Vitest is 4 major versions behind (current: 1.6.1, latest: 5.1.0).
vibgrate/framework-major-lag in apps/api
✖ @types/node is 6 major versions behind (spec: ^20.11.0, latest: 26.6.6).
vibgrate/dependency-major-lag in apps/web
✖ eslint-plugin-react-hooks is 3 major versions behind (spec: ^4.6.0, latest: 7.1.1).
vibgrate/dependency-major-lag in packages/config
✖ 56% of dependencies are 2+ major versions behind in @repo/config.
vibgrate/dependency-rot in packages/config
✖ 75% of dependencies are 2+ major versions behind in @repo/database.
vibgrate/dependency-rot in packages/database
✖ 100% of dependencies are 2+ major versions behind in @repo/types.
vibgrate/dependency-rot in packages/types
✖ vitest is 4 major versions behind (spec: ^1.2.1, latest: 5.1.0).
vibgrate/dependency-major-lag in packages/utils
✖ 67% of dependencies are 2+ major versions behind in @repo/utils.
vibgrate/dependency-rot in packages/utils
✖ Vitest is 4 major versions behind (current: 1.6.1, latest: 5.1.0).
vibgrate/framework-major-lag in packages/utils
⚠ TypeScript is 2 major versions behind (current: 5.9.3, latest: 7.0.2).
vibgrate/framework-major-lag in .
⚠ TypeScript is 2 major versions behind (current: 5.9.3, latest: 7.0.2).
vibgrate/framework-major-lag in apps/admin
⚠ TypeScript is 2 major versions behind (current: 5.9.3, latest: 7.0.2).
vibgrate/framework-major-lag in apps/api
⚠ Next.js is 2 major versions behind (current: 14.2.35, latest: 16.4.6).
vibgrate/framework-major-lag in apps/web
⚠ TypeScript is 2 major versions behind (current: 5.9.3, latest: 7.0.2).
vibgrate/framework-major-lag in apps/web
⚠ TypeScript is 2 major versions behind (current: 5.9.3, latest: 7.0.2).
vibgrate/framework-major-lag in packages/config
⚠ Prisma is 2 major versions behind (current: 5.22.0, latest: 7.10.1).
vibgrate/framework-major-lag in packages/database
⚠ TypeScript is 2 major versions behind (current: 5.9.3, latest: 7.0.2).
vibgrate/framework-major-lag in packages/database
⚠ TypeScript is 2 major versions behind (current: 5.9.3, latest: 7.0.2).
vibgrate/framework-major-lag in packages/types
⚠ TypeScript is 2 major versions behind (current: 5.9.3, latest: 7.0.2).
vibgrate/framework-major-lag in packages/ui
⚠ TypeScript is 2 major versions behind (current: 5.9.3, latest: 7.0.2).
vibgrate/framework-major-lag in packages/utils
 
╭──────────────────────────────────────────╮
│ Top Priority Actions │
╰──────────────────────────────────────────╯
 
1. Upgrade EOL runtime in node-turborepo
End-of-life runtimes no longer receive security patches and block ecosystem upgrades.
./.
>=18.0.0 → 26.0.0 (8 majors behind)
Impact: −10 drift points (runtime & EOL)
 
2. Fix security posture: no lockfile found
Without a lockfile, installs are non-deterministic. Run the install command to generate one and commit it.
./
Missing: package-lock.json, pnpm-lock.yaml, or yarn.lock
 
3. Upgrade Vitest 1.6.1 → 5.1.0 in @repo/api (+2 more)
4 major versions behind. Major framework drift increases breaking change risk and blocks access to security fixes and performance improvements.
./apps/api
Vitest: 1.6.1 → 5.1.0 (4 majors behind)
./packages/utils
Vitest: 1.6.1 → 5.1.0 (4 majors behind)
./apps/admin
Vite: 5.4.21 → 8.3.8 (3 majors behind)
Impact: −5–15 drift points
 
4. Reduce dependency rot in @repo/types (100% severely outdated)
1 of 1 dependencies are 2+ majors behind. Run `npm outdated` and prioritise packages with known CVEs or breaking API changes.
./packages/types
typescript: 5.9.3 → 7.0.2 (2 majors behind)
Impact: −5–10 drift points
 
5. Reduce dependency rot in @repo/database (75% severely outdated)
3 of 4 dependencies are 2+ majors behind. Run `npm outdated` and prioritise packages with known CVEs or breaking API changes.
./packages/database
@prisma/client: 5.22.0 → 7.10.1 (2 majors behind)
prisma: 5.22.0 → 7.10.1 (2 majors behind)
typescript: 5.9.3 → 7.0.2 (2 majors behind)
Impact: −5–10 drift points
 
╭──────────────────────────────────────────╮
│ Architecture Layers │
╰──────────────────────────────────────────╯
 
Archetype: nextjs (80% confidence)
Files classified: 24 (11 unclassified)
Folders classified: 8
apps/admin/src presentation 100% 4 files
apps/admin/src/pages presentation 100% 2 files
apps/api/src/middleware middleware 100% 2 files
apps/api/src/routes routing 100% 2 files
apps/web/src/app presentation 100% 4 files
apps/web/src/app/products presentation 100% 2 files
apps/web/src/app/products/[id] presentation 100% 1 file
packages/ui/src presentation 100% 6 files
Unclassified source (sample): 11
 
presentation 15 files drift ████████████████████ 100 risk high
routing 4 files drift ████████████████████ 100 risk high
middleware 2 files drift ███████▍░░░░░░░░░░░░ 37 risk moderate
config 2 files drift ░░░░░░░░░░░░░░░░░░░░ 0 risk none
shared 1 file drift ████████████████████ 100 risk high
 
╭──────────────────────────────────────────╮
│ DriftScore Summary │
╰──────────────────────────────────────────╯
 
DriftScore: 70/100
Risk Level: HIGH
Projects: 9
Classified: 8 nano · 1 micro · 0 small · 0 standard
Billable: 0.42 · 9 detected → 0.42 billable projects (micro-project pricing)
0.1 micro · 0.32 nano
These fractions add up across repositories, then round down to whole billable projects.
 
Score Breakdown
Runtime: ████████████████████ 100
Frameworks: ███████████▊░░░░░░░░ 59
Dependencies: ███████░░░░░░░░░░░░░ 35
EOL Risk: ████████████████████ 100
 
Scanned at 2026-11-12T23:47:37.520Z · 5.4s · 286 files scanned · 56 workspace files · 27 dirs
❯
Press Run to start.