Designing a Multi-Repository, Multi-Environment GitOps Architecture with ArgoCD

Introduction

Modern Kubernetes delivery pipelines require more than just automation — they demand clarity, separation of concerns, reproducibility, and governance.

This article presents a real-world GitOps architecture built around ArgoCD, designed to support:

  • Multiple environments: dev, staging, prod
  • Multiple applications: devops-api, totp-api
  • Separation of responsibilities across repositories
  • Helm-based deployments with centralized packaging
  • Full Git-driven control of infrastructure and application lifecycle

The goal is not only to describe how this system works, but why each design decision matters.

https://github.com/faustobranco/devops-db/tree/master/infrastructure/argocd/MultiRepo/argocd


Architectural Overview

The system is built around four distinct repository types, each with a clearly defined responsibility:

Helm Source Repos       → define HOW to deploy
Helm Registry (Nexus)   → version and distribute charts
Values Repos            → define WHAT to deploy (configuration)
Applications Repo       → define WHERE and WHEN to deploy

Repository Roles

1. Helm Source Repositories

https://gitlab.devops-db.internal/infrastructure/argocd/helms/devops-api.git
https://gitlab.devops-db.internal/infrastructure/argocd/helms/totp-api.git

These repositories contain:

  • Helm templates
  • Chart definitions
  • Deployment logic (Ingress, Services, etc.)

Why separate this?

Because deployment logic should be immutable and versioned independently from configuration.

This allows:

  • Reusability across environments
  • Stable versioning
  • Clear ownership (platform vs application teams)

2. Helm Registry (Nexus)

https://nexus.devops-db.internal/repository/helm-devops-db/

Charts are packaged and published:

devops-api-1.0.5.tgz
totp-api-1.0.7.tgz

Why use a Helm registry?

  • Promotes immutable artifacts
  • Enables version pinning
  • Avoids direct Git dependency during deployment
  • Aligns with enterprise artifact management practices

3. Values Repositories (Per Application)

https://gitlab.devops-db.internal/infrastructure/argocd/applications/devops-api.git
https://gitlab.devops-db.internal/infrastructure/argocd/applications/totp-api.git

Structure:

envs/
  dev/
  staging/
  prod/

Each environment defines:

  • Canary vs Stable configuration
  • Replica counts
  • Ingress weights
  • Feature toggles

Why separate values?

This is one of the most critical design decisions:

Chart = deployment logic
Values = runtime behavior

This separation enables:

  • Environment-specific overrides
  • Safer promotions
  • Reduced duplication
  • Git-driven rollout control

4. ArgoCD Applications Repository (Control Plane)

https://gitlab.devops-db.internal/infrastructure/argocd/argocd-applications.git

Structure:

applications/
  dev/
  staging/
  prod/
root/
  dev.yaml
  staging.yaml
  prod.yaml

This repository defines:

  • ArgoCD Applications
  • Environment scoping
  • Deployment topology

Why isolate this repo?

Because this is the control plane of your cluster.

It answers:

What runs?
Where does it run?
Which version runs?

Multi-Environment Design

The system supports three environments:

dev
staging
prod

Each environment has:

  • Its own ArgoCD instance
  • Its own root application
  • Its own subset of configurations

Root Application Pattern

Each cluster applies only its own root:

# root/prod.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: root-apps
  namespace: argocd
spec:
  project: default

  source:
    repoURL: https://gitlab.devops-db.internal/infrastructure/argocd/argocd-applications.git
    targetRevision: HEAD
    path: applications/prod
    directory:
      recurse: true

  destination:
    server: https://kubernetes.default.svc
    namespace: argocd

  syncPolicy:
    automated:
      prune: true
      selfHeal: true

Why this matters

This enforces strict isolation:

EnvironmentArgoCD Scope
devapplications/dev
stagingapplications/staging
prodapplications/prod

This prevents:

  • Cross-environment contamination
  • Accidental production deployments
  • Misconfigured rollouts

Multi-Source Applications (Core Concept)

Each application uses ArgoCD multi-source:

sources:
  - repoURL: Nexus (Helm chart)
  - repoURL: GitLab (values)

Example:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: totp-api-canary
  namespace: argocd
spec:
  project: totp-api

  sources:
    # Chart (Nexus)
    - repoURL: https://nexus.devops-db.internal/repository/helm-devops-db/
      chart: totp-api
      targetRevision: 1.0.7
      helm:
        valueFiles:
          - $repo-app-values/envs/prod/values-canary.yaml

    # Values (GitLab)
    - repoURL: https://gitlab.devops-db.internal/infrastructure/argocd/applications/totp-api.git
      targetRevision: HEAD
      ref: repo-app-values

  destination:
    server: https://kubernetes.default.svc
    namespace: devops-api

  syncPolicy:
    automated:
      prune: true
      selfHeal: true

Why this design?

Because deployment requires two different concerns:

  1. Structure (Helm chart)
  2. Configuration (values)

ArgoCD combines both at runtime:

Fetch chart → Fetch values → Render → Apply

Important Detail: $values Reference

ref: repo-app-values

This creates an alias:

$repo-app-values → Git repository

Then:

$repo-app-values/envs/prod/values.yaml

means:

<repo root>/envs/prod/values.yaml

Why is this powerful?

Because it allows:

  • Cross-repo composition
  • Centralized configuration
  • Dynamic environment selection

Deployment Strategy: Canary + Stable

Each application defines:

stable
canary

This enables:

  • Progressive delivery
  • Traffic splitting via Ingress
  • Safe rollouts

Why not use Argo Rollouts?

In this setup:

  • Canary is controlled via Helm + values
  • Simpler and fully Git-driven
  • No extra CRDs required

GitOps Flow

End-to-End Flow

Git Commit (values or applications repo)
        ↓
ArgoCD detects change
        ↓
Fetch chart (Nexus)
Fetch values (Git)
        ↓
Render Helm
        ↓
Apply to Kubernetes

Promotion Flow

Promotion is entirely Git-driven:

dev → staging → prod

Example:

  1. Change values in dev
  2. Validate behavior
  3. Promote to staging
  4. Promote to prod

Why Git-based promotion?

Because it guarantees:

  • Auditability
  • Reproducibility
  • Consistency across environments

Why This Architecture Works

1. Clear Separation of Concerns

ComponentResponsibility
HelmHow to deploy
ValuesWhat to deploy
ApplicationsWhere to deploy

2. Scalability

Adding a new service requires:

  • New Helm chart
  • New values repo
  • New Application YAML

No structural changes needed.


3. Security and Governance

Using ArgoCD AppProjects:

  • Restricts allowed repositories
  • Enforces boundaries
  • Enables multi-team setups

4. Reproducibility

Everything is:

  • Versioned
  • Declarative
  • Git-controlled

5. Flexibility

Supports:

  • Multiple environments
  • Multiple clusters
  • Multiple teams

Leave a Reply

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