# What Is the Argo CD Notification Controller? Architecture and Implementation Guide

> Discover the Argo CD notification controller, a key Kubernetes component for real-time alerts on application lifecycle events. Integrate with Slack, email, and webhooks seamlessly.

- Repository: [Argo Project/argo-cd](https://github.com/argoproj/argo-cd)
- Tags: architecture
- Published: 2026-07-09

---

**The Argo CD notification controller is a dedicated Kubernetes component that bridges Argo CD applications with the external notifications-engine library to deliver real-time alerts about application lifecycle events via Slack, email, webhooks, and other channels.**

The Argo CD notification controller serves as the critical glue between your GitOps workloads and external notification services. As implemented in the `argoproj/argo-cd` repository, this specialized component continuously monitors `Application` and `AppProject` resources, processing state changes and routing alerts according to configurable triggers and templates. Understanding its architecture is essential for customizing notification workflows and troubleshooting delivery issues in production GitOps environments.

## Core Responsibilities of the Notification Controller

### Watching Argo CD Resources with Shared Informers

The controller establishes **shared informers** for four critical resource types: `Application`, `AppProject`, the notifications `ConfigMap`, and the notifications `Secret`. These informers maintain a local cache of cluster state and trigger processing loops when objects change, ensuring immediate reaction to deployment events without polling the Kubernetes API.

In [`notification_controller/controller/controller.go`](https://github.com/argoproj/argo-cd/blob/main/notification_controller/controller/controller.go), the informer setup occurs between lines 48-62, where the controller initializes watchers for each resource type and registers event handlers that enqueue objects for processing.

### Filtering Events and Preventing Stale Notifications

Before dispatching any alert, the controller performs strict validation to ensure relevance and freshness. The implementation checks whether an `Application` belongs to a monitored namespace via `checkAppNotInAdditionalNamespaces` and verifies sync status is current using `isAppSyncStatusRefreshed`. This filtering prevents noise from stale resources or applications outside the controller's scope.

### Integrating with the Notifications Engine

The controller constructs a `notifications-engine` API factory using the current `ConfigMap` and `Secret` via `api.NewFactory`. This factory, initialized in `NewController` (lines 93-96 of [`notification_controller/controller/controller.go`](https://github.com/argoproj/argo-cd/blob/main/notification_controller/controller/controller.go)), supplies runtime configuration including templates, services, and triggers required by the underlying notification engine to format and route messages.

## Implementation Deep Dive

### Controller Initialization and Lifecycle

The controller follows a two-phase startup pattern. First, `Init` prepares the informers and validates configuration. Then, `Run` starts the shared informers and delegates processing to the notifications-engine controller (`c.ctrl.Run`) with a configurable worker pool.

During initialization, the controller also prepares project-aware routing through the `alterDestinations` method (lines 35-45), which merges destinations defined in project annotations with those from the notification configuration. This enables per-project alert routing for multi-tenant environments.

### Project-Aware Destination Routing

When an application belongs to a project, the controller resolves final notification destinations by combining:
- Destinations derived from the notification `ConfigMap` configuration
- Destinations specified in project annotations (supporting both legacy and new formats)

This merging logic ensures that project-specific notification channels take precedence while maintaining global defaults.

### Exposing the gRPC API

The server-side implementation in [`server/notification/notification.go`](https://github.com/argoproj/argo-cd/blob/main/server/notification/notification.go) wraps the notification engine's API factory in a gRPC service. The `NewServer` constructor and `List*` methods (lines 12-68) expose endpoints to query triggers, services, and templates. This API powers the Argo CD UI and CLI when displaying configured notification objects.

## Practical Implementation: Starting the Controller

The following example demonstrates creating and running the notification controller with namespace-scoped configuration:

```go
import (
    "context"
    "k8s.io/client-go/kubernetes"
    "k8s.io/client-go/dynamic"
    service "github.com/argoproj/argo-cd/v3/util/notification/argocd"
    "github.com/argoproj/argo-cd/v3/notification_controller/controller"
)

func startNotificationController(k8sClient kubernetes.Interface, dynClient dynamic.Interface) {
    // Service that provides access to Argo CD Application objects
    argocdSvc := service.NewService(...) // omitted for brevity

    // Create controller (listening on the same namespace as Argo CD)
    ctrl := controller.NewController(
        k8sClient,
        dynClient,
        argocdSvc,
        "argocd",                 // namespace
        nil,                      // applicationNamespaces (empty = all)
        "",                       // appLabelSelector
        nil,                      // metrics registry (optional)
        "argocd-notifications-secret",
        "argocd-notifications-cm",
        false,                    // self‑service notifications disabled
    )

    // Initialise informers and start processing
    ctx := context.TODO()
    if err := ctrl.Init(ctx); err != nil {
        log.Fatalf("failed to initialise notification controller: %v", err)
    }
    go ctrl.Run(ctx, 4) // use 4 processor workers
}

```

## Querying Notification Configuration Programmatically

You can interact with the notification configuration via the gRPC API exposed by the server:

```go
import (
    "context"
    notifpb "github.com/argoproj/argo-cd/v3/pkg/apiclient/notification"
)

func listTriggers(srv notifpb.NotificationServiceServer) {
    resp, err := srv.ListTriggers(context.Background(), &notifpb.TriggersListRequest{})
    if err != nil {
        log.Fatalf("error listing triggers: %v", err)
    }
    for _, t := range resp.Items {
        fmt.Println("Trigger:", t.Name)
    }
}

```

## Key Source Files and Testing

The notification controller implementation spans several critical files in the `argoproj/argo-cd` repository:

- **[`notification_controller/controller/controller.go`](https://github.com/argoproj/argo-cd/blob/main/notification_controller/controller/controller.go)** – Sets up informers, builds the notifications-engine controller, and runs the processing loop.
- **[`server/notification/notification.go`](https://github.com/argoproj/argo-cd/blob/main/server/notification/notification.go)** – Exposes the notification API (list triggers/services/templates) over gRPC.
- **[`util/notification/settings/settings.go`](https://github.com/argoproj/argo-cd/blob/main/util/notification/settings/settings.go)** – Reads the `ConfigMap`/`Secret`, parses templates, services, and triggers.
- **[`util/notification/argocd/service.go`](https://github.com/argoproj/argo-cd/blob/main/util/notification/argocd/service.go)** – Provides the `Service` implementation used by the controller to fetch project information.
- **[`notification_controller/controller/controller_test.go`](https://github.com/argoproj/argo-cd/blob/main/notification_controller/controller/controller_test.go)** – Validates controller behavior including informer sync and destination merging.

## Summary

- The **Argo CD notification controller** watches `Application`, `AppProject`, and configuration resources via shared informers to detect state changes.
- It filters events using `checkAppNotInAdditionalNamespaces` and `isAppSyncStatusRefreshed` to prevent stale or irrelevant notifications.
- The controller builds a notifications-engine API factory using `api.NewFactory` and configuration from the `ConfigMap` and `Secret`.
- Project-aware routing merges destinations from project annotations with global configuration via the `alterDestinations` method.
- A gRPC server in [`server/notification/notification.go`](https://github.com/argoproj/argo-cd/blob/main/server/notification/notification.go) exposes the notification API for UI and CLI consumption.

## Frequently Asked Questions

### What is the primary function of the Argo CD notification controller?

The Argo CD notification controller monitors Kubernetes resources and triggers external notifications when applications change state. It acts as a bridge between Argo CD's GitOps engine and notification services like Slack, email, or webhooks, evaluating triggers and formatting messages according to user-defined templates.

### How does the notification controller handle multi-tenant or namespace-scoped applications?

The controller respects namespace boundaries through the `checkAppNotInAdditionalNamespaces` function, which verifies applications belong to monitored namespaces before processing. Additionally, the `alterDestinations` method enables project-specific notification routing by merging destinations from `AppProject` annotations with global configuration settings.

### Can notification templates and triggers be queried via the Argo CD API?

Yes. The notification controller exposes a gRPC API through [`server/notification/notification.go`](https://github.com/argoproj/argo-cd/blob/main/server/notification/notification.go) that provides methods like `ListTriggers`, `ListServices`, and `ListTemplates`. These endpoints allow the Argo CD CLI and UI to display current notification configurations without directly accessing the Kubernetes API.

### Where does the Argo CD notification controller store its configuration?

Configuration resides in two Kubernetes resources: a `ConfigMap` (typically named `argocd-notifications-cm`) storing templates, triggers, and service definitions, and a `Secret` (typically `argocd-notifications-secret`) containing sensitive data like API tokens or webhook URLs. The controller watches both resources and hot-reloads changes without requiring a restart.