Projects provide a logical grouping of applications with security and operational boundaries. They are especially useful when Argo CD is used by multiple teams.
Overview
AppProject is a Custom Resource Definition (CRD) that provides:
Source restrictions - Control which Git repositories can be deployed
Destination restrictions - Control where apps may be deployed (clusters and namespaces)
Resource restrictions - Control what kinds of Kubernetes objects can be deployed
RBAC - Define project roles with fine-grained permissions
AppProject CRD Structure
apiVersion : argoproj.io/v1alpha1
kind : AppProject
metadata :
name : my-project
namespace : argocd
spec :
description : Project for team applications
# Source repositories
sourceRepos :
- 'https://github.com/myorg/*'
- 'https://charts.helm.sh/stable'
# Destination clusters and namespaces
destinations :
- namespace : 'team-*'
server : 'https://kubernetes.default.svc'
- namespace : 'production'
name : 'prod-cluster'
# Cluster-scoped resource whitelist
clusterResourceWhitelist :
- group : ''
kind : Namespace
# Namespace-scoped resource blacklist
namespaceResourceBlacklist :
- group : ''
kind : ResourceQuota
- group : ''
kind : LimitRange
The Default Project
Every application belongs to a project. If unspecified, applications belong to the default project.
Default Project Specification
The default project is created automatically with permissive settings:
apiVersion : argoproj.io/v1alpha1
kind : AppProject
metadata :
name : default
spec :
sourceRepos :
- '*'
destinations :
- namespace : '*'
server : '*'
clusterResourceWhitelist :
- group : '*'
kind : '*'
The default project can be modified but not deleted. For production use, create dedicated projects with explicit restrictions.
Locking Down Default Project
To prevent accidental use of the overly-permissive default project:
apiVersion : argoproj.io/v1alpha1
kind : AppProject
metadata :
name : default
spec :
sourceRepos : []
destinations : []
namespaceResourceBlacklist :
- group : '*'
kind : '*'
Creating Projects
apiVersion : argoproj.io/v1alpha1
kind : AppProject
metadata :
name : myproject
namespace : argocd
spec :
sourceRepos :
- https://github.com/argoproj/argocd-example-apps.git
destinations :
- server : https://kubernetes.default.svc
namespace : mynamespace
kubectl apply -f project.yaml
argocd proj create myproject \
-d https://kubernetes.default.svc,mynamespace \
-s https://github.com/argoproj/argocd-example-apps.git
Source Repository Restrictions
Control which Git repositories can be used:
Allow Specific Repositories
spec :
sourceRepos :
- 'https://github.com/myorg/frontend-apps'
- 'https://github.com/myorg/backend-apps'
- 'https://charts.bitnami.com/bitnami'
Using Wildcards
spec :
sourceRepos :
- 'https://github.com/myorg/*'
- 'https://gitlab.com/team-*/*'
Denying Repositories
Use ! prefix to explicitly deny repositories:
spec :
sourceRepos :
# Deny specific test repository
- '!ssh://git@github.com:argoproj/test'
# Deny all GitLab repos under group/
- '!https://gitlab.com/group/**'
# Allow everything else
- '*'
A repository is valid if ANY allow rule permits it AND NO deny rule rejects it. The pattern !* is invalid.
Managing Sources via CLI
# Add source repository
argocd proj add-source myproject https://github.com/myorg/myrepo
# Remove source repository
argocd proj remove-source myproject https://github.com/myorg/myrepo
# Add denial pattern
argocd proj add-source myproject '!https://github.com/untrusted/*'
Destination Restrictions
Control where applications can deploy:
Specific Destinations
spec :
destinations :
- server : https://kubernetes.default.svc
namespace : dev
- server : https://kubernetes.default.svc
namespace : staging
- server : https://prod-cluster.example.com
namespace : production
Using Cluster Names
spec :
destinations :
- name : in-cluster
namespace : '*'
- name : prod-cluster
namespace : 'prod-*'
Denying Destinations
spec :
destinations :
# Deny kube-system on all clusters
- namespace : '!kube-system'
server : '*'
# Deny specific cluster pattern
- namespace : '*'
server : '!https://team1-*'
# Allow everything else
- namespace : '*'
server : '*'
Managing Destinations via CLI
# Add destination
argocd proj add-destination myproject \
https://kubernetes.default.svc,mynamespace
# Remove destination
argocd proj remove-destination myproject \
https://kubernetes.default.svc,mynamespace
# Add denial pattern
argocd proj add-destination myproject '!*,!kube-system'
Resource Restrictions
Cluster-Scoped Resources (Whitelist)
Only explicitly allowed cluster-scoped resources can be deployed:
spec :
clusterResourceWhitelist :
- group : ''
kind : Namespace
- group : 'rbac.authorization.k8s.io'
kind : ClusterRole
- group : 'rbac.authorization.k8s.io'
kind : ClusterRoleBinding
Restrict by Name
spec :
clusterResourceWhitelist :
# Only allow namespaces starting with team1-
- group : ''
kind : Namespace
name : 'team1-*'
Namespace-Scoped Resources (Blacklist)
All namespace-scoped resources are allowed except those blacklisted:
spec :
namespaceResourceBlacklist :
- group : ''
kind : ResourceQuota
- group : ''
kind : LimitRange
- group : 'networking.k8s.io'
kind : NetworkPolicy
Managing Resources via CLI
# Allow cluster resource
argocd proj allow-cluster-resource myproject rbac.authorization.k8s.io ClusterRole
# Deny namespace resource
argocd proj deny-namespace-resource myproject "" ResourceQuota
# Deny specific resource by name
argocd proj deny-namespace-resource myproject apps Deployment critical-app
Project Roles and RBAC
Define roles within projects for fine-grained access control:
spec :
roles :
- name : read-only
description : Read-only access to project applications
policies :
- p, proj:myproject:read-only, applications, get, myproject/*, allow
- p, proj:myproject:read-only, applications, list, myproject/*, allow
groups :
- my-oidc-group
- another-group
- name : ci-pipeline
description : CI pipeline sync permissions
policies :
- p, proj:myproject:ci-pipeline, applications, sync, myproject/*, allow
# No groups - accessed via JWT tokens
Policy rules must follow the pattern proj:<project-name>:<role-name> to be effective during authorization.
Available Actions
Roles can control access to multiple resource types:
applications - Application resources
applicationsets - ApplicationSet resources
repositories - Repository credentials
clusters - Cluster credentials
logs - Application logs
exec - Exec into pod containers
Managing Roles via CLI
# Create role
argocd proj role create myproject deployer
# Add policy to role
argocd proj role add-policy myproject deployer \
--action sync \
--permission allow \
--object 'myproject/*'
# List roles
argocd proj role list myproject
# Get role details
argocd proj role get myproject deployer
JWT Tokens for Automation
Generate JWT tokens for programmatic access:
# Create token (with expiration)
argocd proj role create-token myproject ci-pipeline -e 10m
# Create token (no expiration)
argocd proj role create-token myproject ci-pipeline
# List tokens
argocd proj role list myproject
# Revoke token
argocd proj role delete-token myproject ci-pipeline < issued-at-timestam p >
Using JWT Tokens
# Set token as environment variable
export ARGOCD_AUTH_TOKEN =< jwt-token >
# Or use --auth-token flag
argocd app sync myapp --auth-token < jwt-toke n >
Sync Windows
Define time windows when syncs are allowed or denied:
spec :
syncWindows :
# Deny syncs during business hours
- kind : deny
schedule : '0 9-17 * * MON-FRI'
duration : 8h
applications :
- 'prod-*'
namespaces :
- production
# Allow syncs during maintenance window
- kind : allow
schedule : '0 2 * * SAT'
duration : 4h
clusters :
- prod-cluster
manualSync : true
Source Namespaces
Restrict which namespaces can contain ApplicationSet resources:
spec :
sourceNamespaces :
- team-apps
- team-infrastructure
Project-Scoped Repositories and Clusters
Allow developers to add repositories and clusters to projects:
RBAC Configuration
# In argocd-rbac-cm ConfigMap
data :
policy.csv : |
p, proj:my-project:admin, repositories, create, my-project/*, allow
p, proj:my-project:admin, repositories, delete, my-project/*, allow
p, proj:my-project:admin, repositories, update, my-project/*, allow
Adding Project-Scoped Repository
argocd repo add https://github.com/myorg/myrepo \
--project my-project
apiVersion : v1
kind : Secret
metadata :
name : my-repo
namespace : argocd
labels :
argocd.argoproj.io/secret-type : repository
type : Opaque
stringData :
project : my-project
url : https://github.com/myorg/myrepo.git
username : myuser
password : mytoken
When using ApplicationSets with templated projects (containing {{ }}), only non-scoped repositories can be used.
Restrict to Project-Scoped Clusters
Force applications to only use clusters belonging to the same project:
spec :
permitOnlyProjectScopedClusters : true
With this setting, applications can only deploy to clusters that have project: my-project in their Secret.
Global Projects
Define global projects that other projects can inherit from:
# In argocd-cm ConfigMap
apiVersion : v1
kind : ConfigMap
metadata :
name : argocd-cm
data :
globalProjects : |
- labelSelector:
matchExpressions:
- key: environment
operator: In
values:
- production
projectName: global-prod-policies
Projects matching the label selector inherit:
namespaceResourceBlacklist
namespaceResourceWhitelist
clusterResourceBlacklist
clusterResourceWhitelist
syncWindows
sourceRepos
destinations
Destination Service Accounts
Specify service accounts for application deployments:
spec :
destinationServiceAccounts :
- server : https://kubernetes.default.svc
namespace : 'team-*'
defaultServiceAccount : team-deployer
Best Practices
Use descriptive names Name projects after teams or applications for clarity
Principle of least privilege Start restrictive and add permissions as needed
Leverage deny patterns Use ! patterns to explicitly block dangerous operations
Implement sync windows Use sync windows for production change management
Enable project-scoped repos Give teams self-service capabilities with proper RBAC
Use global projects Share common policies across multiple projects