Skip to main content
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

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-timestamp>

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-token>

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
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