Argo CD Projects are the primary mechanism for isolating Teams and their application deployments within a shared Kubernetes cluster.

Let’s see this in action. Imagine we have two teams, frontend and backend, and we want to give them their own isolated environments for deploying their applications managed by Argo CD.

Here’s a Project definition for the frontend team:

apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
  name: frontend
  namespace: argocd
spec:
  clusterResourceBlacklist:
  - '*'
  destinations:
  - namespace: frontend
    server: https://kubernetes.default.svc
  sourceNamespaces:
  - frontend
  roles:
  - name: frontend-admin
    description: Frontend team administrator
    policies:
    - p, p, applications, create, argocd/frontend/*, allow
    - p, p, projects, edit, argocd/frontend, allow
    - p, p, clusters, get, *, allow
    - p, p, namespaces, get, frontend, allow
    - p, p, applications, get, argocd/frontend/*, allow
    - p, p, repositories, get, *, allow
    - p, p, projects, get, frontend, allow
    - p, p, exec, get, argocd/frontend/*, allow
    - p, p, certificates, get, *, allow
    - p, p, secrets, get, *, allow
    - p, p, pods, get, frontend, allow
    - p, p, pods, exec, frontend, allow
    - p, p, pods, log, frontend, allow
    - p, p, pods, delete, frontend, allow
    - p, p, jobs, create, frontend, allow
    - p, p, jobs, delete, frontend, allow
    - p, p, configmaps, get, frontend, allow
    - p, p, configmaps, create, frontend, allow
    - p, p, configmaps, update, frontend, allow
    - p, p, configmaps, delete, frontend, allow
    - p, p, secrets, get, frontend, allow
    - p, p, secrets, create, frontend, allow
    - p, p, secrets, update, frontend, allow
    - p, p, secrets, delete, frontend, allow
    - p, p, services, get, frontend, allow
    - p, p, services, create, frontend, allow
    - p, p, services, update, frontend, allow
    - p, p, services, delete, frontend, allow
    - p, p, deployments, get, frontend, allow
    - p, p, deployments, create, frontend, allow
    - p, p, deployments, update, frontend, allow
    - p, p, deployments, delete, frontend, allow
    - p, p, statefulsets, get, frontend, allow
    - p, p, statefulsets, create, frontend, allow
    - p, p, statefulsets, update, frontend, allow
    - p, p, statefulsets, delete, frontend, allow
    - p, p, daemonsets, get, frontend, allow
    - p, p, daemonsets, create, frontend, allow
    - p, p, daemonsets, update, frontend, allow
    - p, p, daemonsets, delete, frontend, allow
    - p, p, replicasets, get, frontend, allow
    - p, p, replicasets, create, frontend, allow
    - p, p, replicasets, update, frontend, allow
    - p, p, replicasets, delete, frontend, allow
    - p, p, ingresses, get, frontend, allow
    - p, p, ingresses, create, frontend, allow
    - p, p, ingresses, update, frontend, allow
    - p, p, ingresses, delete, frontend, allow
    - p, p, endpoints, get, frontend, allow
    - p, p, endpoints, create, frontend, allow
    - p, p, endpoints, update, frontend, allow
    - p, p, endpoints, delete, frontend, allow
    - p, p, jobs, get, frontend, allow
    - p, p, jobs, update, frontend, allow
    - p, p, cronjobs, get, frontend, allow
    - p, p, cronjobs, create, frontend, allow
    - p, p, cronjobs, update, frontend, allow
    - p, p, cronjobs, delete, frontend, allow
    - p, p, deployments, patch, frontend, allow
    - p, p, statefulsets, patch, frontend, allow
    - p, p, daemonsets, patch, frontend, allow
    - p, p, replicasets, patch, frontend, allow
    - p, p, jobs, patch, frontend, allow
    - p, p, cronjobs, patch, frontend, allow
    - p, p, pods, patch, frontend, allow
    - p, p, services, patch, frontend, allow
    - p, p, ingresses, patch, frontend, allow
    - p, p, endpoints, patch, frontend, allow
    - p, p, configmaps, patch, frontend, allow
    - p, p, secrets, patch, frontend, allow
    - p, p, networkpolicies, get, frontend, allow
    - p, p, networkpolicies, create, frontend, allow
    - p, p, networkpolicies, update, frontend, allow
    - p, p, networkpolicies, delete, frontend, allow
    - p, p, resourcequotas, get, frontend, allow
    - p, p, resourcequotas, create, frontend, allow
    - p, p, resourcequotas, update, frontend, allow
    - p, p, resourcequotas, delete, frontend, allow
    - p, p, limitranges, get, frontend, allow
    - p, p, limitranges, create, frontend, allow
    - p, p, limitranges, update, frontend, allow
    - p, p, limitranges, delete, frontend, allow
    - p, p, pods, create, frontend, allow
    - p, p, pods, update, frontend, allow
    - p, p, pods, delete, frontend, allow
    - p, p, pods, patch, frontend, allow
    - p, p, deployments, create, frontend, allow
    - p, p, deployments, update, frontend, allow
    - p, p, deployments, delete, frontend, allow
    - p, p, deployments, patch, frontend, allow
    - p, p, statefulsets, create, frontend, allow
    - p, p, statefulsets, update, frontend, allow
    - p, p, statefulsets, delete, frontend, allow
    - p, p, statefulsets, patch, frontend, allow
    - p, p, daemonsets, create, frontend, allow
    - p, p, daemonsets, update, frontend, allow
    - p, p, daemonsets, delete, frontend, allow
    - p, p, daemonsets, patch, frontend, allow
    - p, p, replicasets, create, frontend, allow
    - p, p, replicasets, update, frontend, allow
    - p, p, replicasets, delete, frontend, allow
    - p, p, replicasets, patch, frontend, allow
    - p, p, jobs, create, frontend, allow
    - p, p, jobs, update, frontend, allow
    - p, p, jobs, delete, frontend, allow
    - p, p, jobs, patch, frontend, allow
    - p, p, cronjobs, create, frontend, allow
    - p, p, cronjobs, update, frontend, allow
    - p, p, cronjobs, delete, frontend, allow
    - p, p, cronjobs, patch, frontend, allow
    - p, p, ingresses, create, frontend, allow
    - p, p, ingresses, update, frontend, allow
    - p, p, ingresses, delete, frontend, allow
    - p, p, ingresses, patch, frontend, allow
    - p, p, services, create, frontend, allow
    - p, p, services, update, frontend, allow
    - p, p, services, delete, frontend, allow
    - p, p, services, patch, frontend, allow
    - p, p, endpoints, create, frontend, allow
    - p, p, endpoints, update, frontend, allow
    - p, p, endpoints, delete, frontend, allow
    - p, p, endpoints, patch, frontend, allow
    - p, p, configmaps, create, frontend, allow
    - p, p, configmaps, update, frontend, allow
    - p, p, configmaps, delete, frontend, allow
    - p, p, configmaps, patch, frontend, allow
    - p, p, secrets, create, frontend, allow
    - p, p, secrets, update, frontend, allow
    - p, p, secrets, delete, frontend, allow
    - p, p, secrets, patch, frontend, allow
    - p, p, networkpolicies, create, frontend, allow
    - p, p, networkpolicies, update, frontend, allow
    - p, p, networkpolicies, delete, frontend, allow
    - p, p, networkpolicies, patch, frontend, allow
    - p, p, resourcequotas, create, frontend, allow
    - p, p, resourcequotas, update, frontend, allow
    - p, p, resourcequotas, delete, frontend, allow
    - p, p, resourcequotas, patch, frontend, allow
    - p, p, limitranges, create, frontend, allow
    - p, p, limitranges, update, frontend, allow
    - p, p, limitranges, delete, frontend, allow
    - p, p, limitranges, patch, frontend, allow
    groups:
    - frontend-admin

And for the backend team:

apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
  name: backend
  namespace: argocd
spec:
  clusterResourceBlacklist:
  - '*'
  destinations:
  - namespace: backend
    server: https://kubernetes.default.svc
  sourceNamespaces:
  - backend
  roles:
  - name: backend-admin
    description: Backend team administrator
    policies:
    - p, p, applications, create, argocd/backend/*, allow
    - p, p, projects, edit, argocd/backend, allow
    - p, p, clusters, get, *, allow
    - p, p, namespaces, get, backend, allow
    - p, p, applications, get, argocd/backend/*, allow
    - p, p, repositories, get, *, allow
    - p, p, projects, get, backend, allow
    - p, p, exec, get, argocd/backend/*, allow
    - p, p, certificates, get, *, allow
    - p, p, secrets, get, *, allow
    - p, p, pods, get, backend, allow
    - p, p, pods, exec, backend, allow
    - p, p, pods, log, backend, allow
    - p, p, pods, delete, backend, allow
    - p, p, jobs, create, backend, allow
    - p, p, jobs, delete, backend, allow
    - p, p, configmaps, get, backend, allow
    - p, p, configmaps, create, backend, allow
    - p, p, configmaps, update, backend, allow
    - p, p, configmaps, delete, backend, allow
    - p, p, secrets, get, backend, allow
    - p, p, secrets, create, backend, allow
    - p, p, secrets, update, backend, allow
    - p, p, secrets, delete, backend, allow
    - p, p, services, get, backend, allow
    - p, p, services, create, backend, allow
    - p, p, services, update, backend, allow
    - p, p, services, delete, backend, allow
    - p, p, deployments, get, backend, allow
    - p, p, deployments, create, backend, allow
    - p, p, deployments, update, backend, allow
    - p, p, deployments, delete, backend, allow
    - p, p, statefulsets, get, backend, allow
    - p, p, statefulsets, create, backend, allow
    - p, p, statefulsets, update, backend, allow
    - p, p, statefulsets, delete, backend, allow
    - p, p, daemonsets, get, backend, allow
    - p, p, daemonsets, create, backend, allow
    - p, p, daemonsets, update, backend, allow
    - p, p, daemonsets, delete, backend, allow
    - p, p, replicasets, get, backend, allow
    - p, p, replicasets, create, backend, allow
    - p, p, replicasets, update, backend, allow
    - p, p, replicasets, delete, backend, allow
    - p, p, ingresses, get, backend, allow
    - p, p, ingresses, create, backend, allow
    - p, p, ingresses, update, backend, allow
    - p, p, ingresses, delete, backend, allow
    - p, p, endpoints, get, backend, allow
    - p, p, endpoints, create, backend, allow
    - p, p, endpoints, update, backend, allow
    - p, p, endpoints, delete, backend, allow
    - p, p, jobs, get, backend, allow
    - p, p, jobs, update, backend, allow
    - p, p, cronjobs, get, backend, allow
    - p, p, cronjobs, create, backend, allow
    - p, p, cronjobs, update, backend, allow
    - p, p, cronjobs, delete, backend, allow
    - p, p, deployments, patch, backend, allow
    - p, p, statefulsets, patch, backend, allow
    - p, p, daemonsets, patch, backend, allow
    - p, p, replicasets, patch, backend, allow
    - p, p, jobs, patch, backend, allow
    - p, p, cronjobs, patch, backend, allow
    - p, p, pods, patch, backend, allow
    - p, p, services, patch, backend, allow
    - p, p, ingresses, patch, backend, allow
    - p, p, endpoints, patch, backend, allow
    - p, p, configmaps, patch, backend, allow
    - p, p, secrets, patch, backend, allow
    - p, p, networkpolicies, get, backend, allow
    - p, p, networkpolicies, create, backend, allow
    - p, p, networkpolicies, update, backend, allow
    - p, p, networkpolicies, delete, backend, allow
    - p, p, resourcequotas, get, backend, allow
    - p, p, resourcequotas, create, backend, allow
    - p, p, resourcequotas, update, backend, allow
    - p, p, resourcequotas, delete, backend, allow
    - p, p, limitranges, get, backend, allow
    - p, p, limitranges, create, backend, allow
    - p, p, limitranges, update, backend, allow
    - p, p, limitranges, delete, backend, allow
    - p, p, pods, create, backend, allow
    - p, p, pods, update, backend, allow
    - p, p, pods, delete, backend, allow
    - p, p, pods, patch, backend, allow
    - p, p, deployments, create, backend, allow
    - p, p, deployments, update, backend, allow
    - p, p, deployments, delete, backend, allow
    - p, p, deployments, patch, backend, allow
    - p, p, statefulsets, create, backend, allow
    - p, p, statefulsets, update, backend, allow
    - p, p, statefulsets, delete, backend, allow
    - p, p, statefulsets, patch, backend, allow
    - p, p, daemonsets, create, backend, allow
    - p, p, daemonsets, update, backend, allow
    - p, p, daemonsets, delete, backend, allow
    - p, p, daemonsets, patch, backend, allow
    - p, p, replicasets, create, backend, allow
    - p, p, replicasets, update, backend, allow
    - p, p, replicasets, delete, backend, allow
    - p, p, replicasets, patch, backend, allow
    - p, p, jobs, create, backend, allow
    - p, p, jobs, update, backend, allow
    - p, p, jobs, delete, backend, allow
    - p, p, jobs, patch, backend, allow
    - p, p, cronjobs, create, backend, allow
    - p, p, cronjobs, update, backend, allow
    - p, p, cronjobs, delete, backend, allow
    - p, p, cronjobs, patch, backend, allow
    - p, p, ingresses, create, backend, allow
    - p, p, ingresses, update, backend, allow
    - p, p, ingresses, delete, backend, allow
    - p, p, ingresses, patch, backend, allow
    - p, p, services, create, backend, allow
    - p, p, services, update, backend, allow
    - p, p, services, delete, backend, allow
    - p, p, services, patch, backend, allow
    - p, p, endpoints, create, backend, allow
    - p, p, endpoints, update, backend, allow
    - p, p, endpoints, delete, backend, allow
    - p, p, endpoints, patch, backend, allow
    - p, p, configmaps, create, backend, allow
    - p, p, configmaps, update, backend, allow
    - p, p, configmaps, delete, backend, allow
    - p, p, configmaps, patch, backend, allow
    - p, p, secrets, create, backend, allow
    - p, p, secrets, update, backend, allow
    - p, p, secrets, delete, backend, allow
    - p, p, secrets, patch, backend, allow
    - p, p, networkpolicies, create, backend, allow
    - p, p, networkpolicies, update, backend, allow
    - p, p, networkpolicies, delete, backend, allow
    - p, p, networkpolicies, patch, backend, allow
    - p, p, resourcequotas, create, backend, allow
    - p, p, resourcequotas, update, backend, allow
    - p, p, resourcequotas, delete, backend, allow
    - p, p, resourcequotas, patch, backend, allow
    - p, p, limitranges, create, backend, allow
    - p, p, limitranges, update, backend, allow
    - p, p, limitranges, delete, backend, allow
    - p, p, limitranges, patch, backend, allow
    groups:
    - backend-admin

The clusterResourceBlacklist: ['*'] is crucial here. It means that no cluster-scoped resources (like ClusterRole or Namespace itself) can be created or managed by applications within this project. This prevents one team from accidentally or maliciously affecting other teams’ environments at the cluster level.

The destinations field specifies where applications associated with this project can be deployed. Here, frontend applications can only go to the frontend namespace on the default Kubernetes API server. Similarly, backend applications are restricted to the backend namespace.

The sourceNamespaces field dictates which namespaces Argo CD can pull Helm values or Kustomize overlays from. This is less about deployment isolation and more about managing the source of truth for application configurations, ensuring that a team’s configuration sources are also kept within their designated namespace.

The roles section defines RBAC within the project. For instance, the frontend-admin role has specific policies that allow it to create applications only within argocd/frontend/* (meaning applications prefixed with frontend/ in the argocd namespace), edit the frontend project itself, and get information about clusters and namespaces, but importantly, only get access to the frontend namespace. It cannot get or manage the backend namespace.

When you apply these AppProject resources to your Argo CD instance (typically in the argocd namespace), and then create an Application resource for a frontend service, you’d specify the project like this:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: my-frontend-app
  namespace: argocd # The Application CR lives in the argocd namespace
spec:
  project: frontend # This links the application to the frontend AppProject
  source:
    repoURL: 'https://github.com/my-org/my-frontend-repo.git'
    targetRevision: HEAD
    path: frontend/overlays/production
  destination:
    namespace: frontend # This must match a destination in the frontend AppProject
    server: 'https://kubernetes.default.svc'
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

If a user assigned to the frontend-admin role tries to create an application targeting the backend namespace, or an application whose name doesn’t match the argocd/frontend/* pattern, Argo CD’s RBAC will deny the request. This enforced separation is the core benefit of using Projects for team isolation.

The true power of Argo CD Projects lies in their ability to act as a control plane for your application deployments. They don’t just restrict where an application can go, but also what it can do and where it can pull its configuration from. You can define multiple destinations within a single project if a team needs to deploy to different namespaces or even different clusters. You can also define multiple roles within a project, giving finer-grained permissions to different members of the same team (e.g., a read-only role vs. an admin role).

What many users overlook is the nuance in the clusterResourceBlacklist and clusterResourceWhitelist fields. While clusterResourceBlacklist: ['*'] is common for isolation, you might have a specific need to allow a single cluster-scoped resource, like a ServiceAccount in a shared namespace, by adding it to a clusterResourceWhitelist within that project definition. This allows for controlled access to essential cluster-wide resources without broadly opening up the cluster.

Once you have multiple AppProject resources defining distinct teams, the next logical step is to consider how these teams’ applications might interact, leading you to explore Kubernetes Network Policies and how they can be integrated into your GitOps workflows.

Want structured learning?

Take the full Argocd course →