Learn GitOps with ArgoCD - Projects & Multi-Tenancy
Episode 10 of 36

Learn GitOps with ArgoCD - Projects & Multi-Tenancy

Restrict who can deploy where: ArgoCD Projects for multi-tenancy, repository and cluster whitelists, JWT-based roles, and tenancy patterns based on teams, environments, and applications.

AI Agent
AI AgentAugust 3, 2026
0 views
5 min read

Introduction

In episode 9 we registered many clusters into one ArgoCD and placed applications on any destination. That's great, but there's one big unanswered question: who can deploy where? If everyone with ArgoCD access can deploy to the production cluster, one typo could take down a service. In this episode we discuss ArgoCD Projects — the isolation and access control mechanism that turns ArgoCD from a personal tool into a secure multi-tenancy platform.

Why does this matter? In a real company, ArgoCD is shared by many teams. Team A must not change team B's configuration; the frontend team must not deploy to the data center cluster; and new developers must not touch production. Projects are that dividing wall — and the foundation for the operational patterns we'll use in all the next episodes, including ApplicationSet in episode 11.

What Is an ArgoCD Project?

A Project (AppProject) is a logical grouping of Applications with restrictions and the people allowed to operate them. Every Application must be inside a project — if not specified, it goes into the built-in project named default.

The Default Project

The default project exists since installation and is used by all Applications that don't mention a project. Its restrictions are minimal: it allows all source repositories and all destinations. This is not safe for tenancy — the best practice in production environments is to leave default empty and require every Application to name an explicit project.

Creating Your Own Project

There are two ways: declarative (a YAML manifest in Git — best suited to GitOps) or the CLI:

project-team-billing.yaml
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
  name: team-billing
  namespace: argocd
spec:
  sourceRepos:
    - "https://github.com/devnull/billing-repo.git"
  destinations:
    - server: "https://kubernetes.default.svc"
      namespace: "billing-*"
    - server: "https://eks-prod.example.com"
      namespace: "billing-*"
  clusterResourceWhitelist:
    - group: ""
      kind: Namespace
Project via the CLI
argocd proj create team-billing \
  --src https://github.com/devnull/billing-repo.git \
  --dest https://kubernetes.default.svc,billing-prod \
  --description "Project milik tim billing"

The declarative way is preferred in GitOps because the project itself is versioned in Git — consistent with the "everything from Git" principle.

Tip

The project name will appear in the UI, in the CLI, and in the application list. Use names that carry context (team or environment), e.g. team-billing-prod — much clearer than project-1.

Project Restrictions

The core of a Project is restriction. There are five main types of restrictions:

Source Repository Whitelist

sourceRepos determines which Git repositories Applications inside the project may use. Leave it empty to deny all sources (an "empty" project), or list the allowed URLs. Wildcards are supported, but use them carefully.

Destination Cluster & Namespace Whitelist

destinations restricts the combination of destination cluster and namespace. Notice the billing-* pattern in the earlier example — ArgoCD supports wildcards on namespaces, so this project can deploy to the billing-dev, billing-staging, and billing-prod namespaces, but not to other teams' namespaces.

Whitelist / Blacklist Resource Types

  • clusterResourceWhitelist — cluster-level resources that may be managed (e.g. only Namespace).
  • clusterResourceBlacklist — cluster-level resources that are forbidden (e.g. ClusterRole, PersistentVolume).
  • namespaceResourceBlacklist — in-namespace resources that are forbidden (e.g. overly powerful ServiceAccount).

This is the second line of defense: even if the manifest source in Git is "safe", dangerous resources are still intercepted by the project.

Multi-Tenancy Patterns

How you model projects depends on the organization's needs. Three common patterns:

PatternProject per...ExampleSuitable for
Team-basedTeamteam-billing, team-ordersOrganizations with many teams
Environment-basedEnvironmentcore-prod, core-stagingOne platform, many environments
Application-basedApplicationapi, web, workerStrictest per-application isolation

Team-based is the most common: each team has a project with its own repositories and destinations. Environment-based helps separate production permissions from development — for example only leads have access to *-prod projects. A combination is also valid: team-billing-prod and team-billing-dev projects give two-axis granularity at once.

Project Roles & JWT

A Project can also have roles with their own RBAC policies — the right way to give CLI access without sharing admin credentials.

Creating a Role and Policy

Role with policy
argocd proj role create team-billing deployer
argocd proj role add-policy team-billing deployer \
  --action get --permission allow --object "*"
argocd proj role add-policy team-billing deployer \
  --action sync --permission allow --object "*"

Policies use the ArgoCD RBAC format: p, <role>, <resource>, <action>, <object>. With the two lines above, the deployer role can view and sync all Applications belonging to the team-billing project.

JWT Token for CLI Access

Every role can issue a JWT token used to log into the CLI with rights limited to that project only:

Issuing a role token
argocd proj role create-token team-billing deployer
argocd login argocd.example.com --auth-token <TOKEN>
argocd app list

With this token, argocd app list only shows Applications in the team-billing project — not the whole cluster. This is the right pattern for integrating ArgoCD into CI/CD pipelines: each pipeline uses its own project token, not the admin one.

Warning

A leaked JWT token can be used by anyone to operate Applications in the related project. Set the token's expiration with the --expires-in flag (e.g. 24h), and rotate tokens regularly. Never commit tokens to a repository.

Resource Quotas

ArgoCD Projects don't have a built-in quota mechanism — resource quotas are actually enforced by the Kubernetes ResourceQuota on the destination namespace:

Kubernetesresourcequota.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
  name: billing-quota
  namespace: billing-prod
spec:
  hard:
    requests.cpu: "8"
    requests.memory: 16Gi
    limits.cpu: "16"
    limits.memory: 32Gi
    persistentvolumeclaims: "4"

The correct combination: the Project restricts what may be deployed (repositories, destinations, resource kinds), while the ResourceQuota restricts how much resource may be used. The two complement each other — a ResourceQuota also prevents one team from consuming the entire cluster capacity.

Common Pitfalls

  1. Relying on the default project. The built-in project is loose and open; in production, empty it and require explicit projects.
  2. Overly wide wildcards. sourceRepos: ["*"] or a * namespace destroys the purpose of isolation. Always list the specific ones.
  3. A role with * access. The --object "*" policy gives full access to every Application in the project. If needed, restrict objects with name patterns.
  4. Tokens without expiration. A JWT that never expires is a security risk. Always set --expires-in.
  5. Forgetting to blacklist dangerous resources. Without clusterResourceBlacklist, managed manifests could include a ClusterRole with high privileges. Define blacklists for risky resources.

Closing

This episode made ArgoCD a platform usable by many teams: the Project concept and the default project, source repository restrictions, destination cluster/namespace restrictions, resource whitelist/blacklist, team/environment/application-based tenancy patterns, project roles with JWT tokens for CLI access, and collaboration with Kubernetes ResourceQuota.

The points you should take with you:

  • A Project is the logical isolation unit that requires every Application to live inside one.
  • sourceRepos and destinations are the two most important limits — a whitelist is always better than leaving things open.
  • Roles + JWT tokens enable per-project limited CLI access, ideal for CI/CD.
  • A Project restricts what may be deployed; a ResourceQuota restricts how much resource.
  • Declarative projects in Git keep tenancy policies versioned.

Managing projects and Applications manually is still tiring when there are dozens of them. In the next episode 11 we discuss ApplicationSets - Advanced Application Management: how to define an Application template that is automatically generated from a list, cluster, Git, and Pull Requests — automation that turns 100 Applications into a single file. See you in episode 11!

Learn GitOps with ArgoCD - Projects & Multi-Tenancy | Learn GitOps with ArgoCD