Explore emerging and supplemental capabilities in Red Hat Developer Hub(Preview)
Learn about features that are new, evolving, or not yet fully covered in the core product documentation
Abstract
1. Integrate Argo CD notifications with Red Hat Developer Hub
This guide explains how to configure Argo CD to send notifications directly to the Red Hat Developer Hub (RHDH) Notifications plugin when application sync events occur.
Administrative requirements: This integration requires infrastructure-level access. To proceed, you must have:
-
cluster-adminpermissions on Red Hat OpenShift Container Platform. - Write access to the RHDH source code or configuration.
This task is intended for Platform Engineers; it involves cluster-wide runtime changes.
Overview
The following diagram illustrates the flow from an Argo CD event in the cluster to the RHDH user interface.
┌────────────────────────────────────────────────────────────────────┐
│ Kubernetes / OpenShift Cluster │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Argo CD │ │
│ │ ┌─────────────────┐ ┌────────────────────────────────┐ │ │
│ │ │ Your App │────▶│ Notifications Controller │ │ │
│ │ │ (with annotations) │ │ │ │
│ │ └─────────────────┘ └───────────────┬────────────────┘ │ │
│ └──────────────────────────────────────────┼──────────────────┘ │
└─────────────────────────────────────────────┼──────────────────────┘
│ Webhook POST
│ (with auth token)
▼
┌────────────────────────────────────────────┐
│ Backstage │
│ Endpoint: /api/notifications/notifications│
│ ──────────────────────────────────────────│
│ Shows notifications to users in the UI │
└────────────────────────────────────────────┘Prerequisites
Before you begin, make sure you have the following:
- Administrative access to the OpenShift cluster where Argo CD and RHDH are running.
- Argo CD installed with the notifications controller enabled.
- Network connectivity from the Argo CD namespace to the RHDH endpoint.
Part 1: Enable the notifications plugin in RHDH
Environment: RHDH Configuration.
Step 1: Enable the dynamic plugins
Add the following to your app-config.yaml or your custom RHDH ConfigMap:
dynamicPlugins:
frontend:
backstage-plugin-notifications:
enabled: true
backend:
backstage-plugin-notifications-backend:
enabled: trueStep 2: Generate and configure a static external access token
To allow Argo CD to authenticate with the RHDH backend, you must provide a secure token.
You can generate a random, unique string to use as a token. In your app-config.yaml, configure the token under the externalAccess section:
backend:
auth:
externalAccess:
- type: static
options:
token: ${ARGOCD_NOTIFICATION_TOKEN} # Set this environment variable
subject: argocd-notificationsSecurity Note: In production, use a strong, randomly generated token and store it securely.
Part 2: Argo CD configuration
Environment: OpenShift Cluster (executing oc commands).
Once RHDH is configured to receive notifications, configure the Argo CD notifications controller to send them.
Step 1: Enable the notifications controller
For OpenShift GitOps:
oc patch argocd openshift-gitops -n openshift-gitops --type merge -p '
{
"spec": {
"notifications": {
"enabled": true
}
}
}'For standard Argo CD, ensure the notifications controller is deployed.
Step 2: Create the notifications Secret
Store the RHDH authentication token in a cluster secret:
apiVersion: v1 kind: Secret metadata: name: argocd-notifications-secret namespace: openshift-gitops # or argocd type: Opaque stringData: backstage-token: 'your-secret-token-here' # Must match ARGOCD_NOTIFICATION_TOKEN
Apply it:
kubectl apply -f argocd-notifications-secret.yaml
Step 3: Configure notifications
Important for OpenShift GitOps Users: The OpenShift GitOps operator manages the
argocd-notifications-cmConfigMap. If you edit the ConfigMap directly, your changes may be overwritten. Instead, always use theNotificationsConfigurationCustom Resource (CR) as shown below.
Option A: For OpenShift GitOps
Use the NotificationsConfiguration Custom Resource:
This is a customizable template
The configuration below is a starting template that you can customize to fit your needs:
- Add triggers: You can add more triggers for other events (for example,
app.status.sync.status == 'Unknown',app.status.health.status == 'Healthy',app.status.health.status == 'Missing', sync running)- Remove triggers: Feel free to remove any triggers you don’t need.
- Customize templates: Modify the notification titles, descriptions, severity levels, and topics to match your organization’s preferences.
- Adjust conditions: Modify the
whenexpressions to fine-tune when notifications are sent.For a complete list of available trigger conditions and variables, see the Argo CD Notifications Triggers documentation.
# Get your Argo CD URL first
ARGOCD_URL=$(oc get routes -n openshift-gitops openshift-gitops-server -o jsonpath='https://{.spec.host}')
BACKSTAGE_URL="https://your-backstage-url.com"
oc apply -n openshift-gitops -f - <<EOF
apiVersion: argoproj.io/v1alpha1
kind: NotificationsConfiguration
metadata:
name: default-notifications-configuration
namespace: openshift-gitops
spec:
# Context provides variables available in templates
context:
argocdUrl: ${ARGOCD_URL}
# Webhook service configuration
services:
service.webhook.backstage: |
url: ${BACKSTAGE_URL}/api/notifications/notifications
headers:
- name: Content-Type
value: application/json
- name: Authorization
value: Bearer \$backstage-token
# Notification templates
# Note: We use custom names with '-backstage' suffix to avoid conflicts with default templates
templates:
template.app-sync-succeeded-backstage: |
webhook:
backstage:
method: POST
body: |
{
"recipients": {
"type": "entity",
"entityRef": "{{index .app.metadata.annotations "backstage.io/entity-ref"}}"
},
"payload": {
"title": "{{.app.metadata.name}} - Sync Succeeded",
"description": "Application {{.app.metadata.name}} has been synced successfully.\n\nHealth: {{.app.status.health.status}}\nRevision: {{.app.status.sync.revision}}",
"link": "{{.context.argocdUrl}}/applications/{{.app.metadata.name}}",
"severity": "low",
"topic": "argocd.sync.succeeded"
}
}
template.app-sync-failed-backstage: |
webhook:
backstage:
method: POST
body: |
{
"recipients": {
"type": "entity",
"entityRef": "{{index .app.metadata.annotations "backstage.io/entity-ref"}}"
},
"payload": {
"title": "{{.app.metadata.name}} - Sync Failed",
"description": "Application {{.app.metadata.name}} sync has failed.\n\nPhase: {{.app.status.operationState.phase}}\nMessage: {{.app.status.operationState.message}}",
"link": "{{.context.argocdUrl}}/applications/{{.app.metadata.name}}",
"severity": "high",
"topic": "argocd.sync.failed"
}
}
template.app-health-degraded-backstage: |
webhook:
backstage:
method: POST
body: |
{
"recipients": {
"type": "entity",
"entityRef": "{{index .app.metadata.annotations "backstage.io/entity-ref"}}"
},
"payload": {
"title": "{{.app.metadata.name}} - Health Degraded",
"description": "Application {{.app.metadata.name}} health has degraded.\n\nStatus: {{.app.status.health.status}}",
"link": "{{.context.argocdUrl}}/applications/{{.app.metadata.name}}",
"severity": "high",
"topic": "argocd.health.degraded"
}
}
template.app-deployed-backstage: |
webhook:
backstage:
method: POST
body: |
{
"recipients": {
"type": "entity",
"entityRef": "{{index .app.metadata.annotations "backstage.io/entity-ref"}}"
},
"payload": {
"title": "{{.app.metadata.name}} - Deployed",
"description": "Application {{.app.metadata.name}} has been successfully deployed and is healthy.",
"link": "{{.context.argocdUrl}}/applications/{{.app.metadata.name}}",
"severity": "low",
"topic": "argocd.deployed"
}
}
triggers:
trigger.on-sync-succeeded-backstage: |
- when: app.status.operationState.phase in ['Succeeded']
send: [app-sync-succeeded-backstage]
trigger.on-sync-failed-backstage: |
- when: app.status.operationState.phase in ['Error', 'Failed']
send: [app-sync-failed-backstage]
trigger.on-health-degraded-backstage: |
- when: app.status.health.status == 'Degraded'
send: [app-health-degraded-backstage]
trigger.on-deployed-backstage: |
- when: app.status.operationState.phase in ['Succeeded'] and app.status.health.status == 'Healthy'
send: [app-deployed-backstage]
EOFOption B: For Standard Argo CD
For non-OpenShift installations, you can edit the ConfigMap directly:
apiVersion: v1
kind: ConfigMap
metadata:
name: argocd-notifications-cm
namespace: argocd
data:
# Context for template variables
context: |
argocdUrl: https://your-argocd-url.com
# Webhook service configuration
service.webhook.backstage: |
url: https://your-backstage-url.com/api/notifications/notifications
headers:
- name: Content-Type
value: application/json
- name: Authorization
value: Bearer $backstage-token
template.app-deployed-backstage: |
webhook:
backstage:
method: POST
body: |
{
"recipients": {
"type": "entity",
"entityRef": "{{ index .app.metadata.annotations backstage.io/entity-ref }}"
},
"payload": {
"title": "{{ .app.metadata.name }} - Deployed",
"description": "Application deployed successfully.",
"link": "{{ .context.argocdUrl }}/applications/{{ .app.metadata.name }}",
"severity": "low",
"topic": "argocd.deployed"
}
}
template.app-health-degraded-backstage: |
webhook:
backstage:
method: POST
body: |
{
"recipients": {
"type": "entity",
"entityRef": "{{ index .app.metadata.annotations backstage.io/entity-ref }}"
},
"payload": {
"title": "{{ .app.metadata.name }} - Health Degraded",
"description": "Application health degraded.",
"link": "{{ .context.argocdUrl }}/applications/{{ .app.metadata.name }}",
"severity": "high",
"topic": "argocd.health.degraded"
}
}
template.app-sync-failed-backstage: |
webhook:
backstage:
method: POST
body: |
{
"recipients": {
"type": "entity",
"entityRef": "{{ index .app.metadata.annotations backstage.io/entity-ref }}"
},
"payload": {
"title": "{{ .app.metadata.name }} - Sync Failed",
"description": "Application sync has failed.",
"link": "{{ .context.argocdUrl }}/applications/{{ .app.metadata.name }}",
"severity": "high",
"topic": "argocd.sync.failed"
}
}
template.app-sync-succeeded-backstage: |
webhook:
backstage:
method: POST
body: |
{
"recipients": {
"type": "entity",
"entityRef": "{{ index .app.metadata.annotations backstage.io/entity-ref }}"
},
"payload": {
"title": "{{ .app.metadata.name }} - Sync Succeeded",
"description": "Application has been synced successfully.",
"link": "{{ .context.argocdUrl }}/applications/{{ .app.metadata.name }}",
"severity": "low",
"topic": "argocd.sync.succeeded"
}
}
trigger.on-deployed-backstage: >
- when: app.status.operationState.phase == 'Succeeded' and
app.status.health.status == 'Healthy'
send: [app-deployed-backstage]
trigger.on-health-degraded-backstage: |
- when: app.status.health.status == 'Degraded'
send: [app-health-degraded-backstage]
trigger.on-sync-failed-backstage: |
- when: app.status.operationState.phase in ['Error', 'Failed']
send: [app-sync-failed-backstage]
trigger.on-sync-succeeded-backstage: |
- when: app.status.operationState.phase == 'Succeeded'
send: [app-sync-succeeded-backstage]Apply it:
kubectl apply -f argocd-notifications-cm.yaml
Part 3: Enable notifications for your application
Environment: OpenShift Cluster (Argo CD Application manifests).
Update individual Argo CD Application manifests to enable the notifications for specific components.
Add required annotations to your Argo CD application
Each Argo CD Application that should send notifications needs two things:
- The Backstage entity reference - tells Argo CD which Backstage entity should receive notifications.
- Trigger subscriptions - tells Argo CD which events to notify about.
The subscription annotation format is notifications.argoproj.io/subscribe.<trigger-name>.<service>. The trigger name must match exactly what you configured (with the -backstage suffix).
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-application
namespace: openshift-gitops
annotations:
# 1. Map to Backstage entity (REQUIRED)
backstage.io/entity-ref: 'component:default/my-application'
# 2. Subscribe to notification triggers (add the ones you want)
notifications.argoproj.io/subscribe.on-sync-succeeded-backstage.backstage: ''
notifications.argoproj.io/subscribe.on-sync-failed-backstage.backstage: ''
notifications.argoproj.io/subscribe.on-health-degraded-backstage.backstage: ''
notifications.argoproj.io/subscribe.on-deployed-backstage.backstage: ''
spec:
# ... your application spec
Example: Complete Setup Flow
Scenario
You have a service called order-service managed by Argo CD, and you want the team to receive Backstage notifications on sync events.
Backstage Catalog Entity
# order-service/catalog-info.yaml apiVersion: backstage.io/v1alpha1 kind: Component metadata: name: order-service description: Order processing service annotations: argocd/app-selector: 'app.kubernetes.io/instance=order-service' spec: type: service owner: team-orders lifecycle: productionArgo CD Application
# argocd/order-service-app.yaml apiVersion: argoproj.io/v1alpha1 kind: Application metadata: name: order-service namespace: openshift-gitops annotations: # Link to Backstage entity backstage.io/entity-ref: 'component:default/order-service' # Subscribe to notifications (note the -backstage suffix in trigger names) notifications.argoproj.io/subscribe.on-deployed-backstage.backstage: '' notifications.argoproj.io/subscribe.on-sync-failed-backstage.backstage: '' notifications.argoproj.io/subscribe.on-health-degraded-backstage.backstage: '' spec: project: default source: repoURL: https://github.com/myorg/order-service targetRevision: main path: k8s/overlays/production destination: server: https://kubernetes.default.svc namespace: order-service syncPolicy: automated: prune: true selfHeal: trueWhat Happens
- Developer pushes code → Argo CD detects change and syncs.
-
Sync succeeds → Argo CD notifications controller fires
on-deployed-backstagetrigger. -
Webhook sent → POST to Backstage
/api/notifications/notifications. - Notification appears → Team sees "🚀 order-service - Deployed" in Backstage with a link to Argo CD.
2. Troubleshooting
2.1. Notifications not appearing in RHDH
Check notifications controller is running:
kubectl get pods -n openshift-gitops | grep notification
Check controller logs for errors:
kubectl logs -n openshift-gitops -l app.kubernetes.io/name=argocd-notifications-controller --tail=50
Check logs for backstage-related messages:
kubectl logs -n openshift-gitops deployment/openshift-gitops-notifications-controller --tail=50 | grep -i backstage
Verify webhook service is configured:
kubectl get configmap argocd-notifications-cm -n openshift-gitops -o jsonpath='{.data.service\.webhook\.backstage}'Verify annotations on application:
kubectl get application my-app -n openshift-gitops -o jsonpath='{.metadata.annotations}' | jq .
2.2. "notification service 'backstage' is not supported"
This error means the webhook service is not configured.
Common causes include the following:
-
OpenShift GitOps: You edited the ConfigMap directly instead of using
NotificationsConfigurationCR. The operator overwrites ConfigMap changes. -
Missing service configuration: The
service.webhook.backstagekey is not in the ConfigMap.
Solution: Use the NotificationsConfiguration CR for OpenShift GitOps (see Part 2).
2.3. Webhook Returns 400 Bad Request / "not valid JSON"
This means the trigger is using the wrong template format (text instead of JSON).
Common causes include the following:
-
Template name conflicts: Using names like
template.app-sync-succeededthat conflict with Argo CD defaults (which use email/message format, not webhook). - Wrong subscription: Application is subscribed to the default trigger instead of your custom one.
Solution: Use custom template/trigger names with a suffix like -backstage:
-
Template:
template.app-sync-succeeded-backstage -
Trigger:
trigger.on-sync-succeeded-backstage -
Subscription:
notifications.argoproj.io/subscribe.on-sync-succeeded-backstage.backstage
2.4. Link Shows <no value> or Broken URL
The {{.context.argocdUrl}} template variable is not configured.
Solution: Add the context configuration:
For OpenShift GitOps:
ARGOCD_URL=$(oc get routes -n openshift-gitops openshift-gitops-server -o jsonpath='https://{.spec.host}')
oc patch notificationsconfiguration default-notifications-configuration -n openshift-gitops --type=merge -p "
spec:
context:
argocdUrl: $ARGOCD_URL
"For standard Argo CD, add to ConfigMap:
data:
context: |
argocdUrl: https://your-argocd-url.com2.5. Authentication Errors
If you see 401 Unauthorized or token errors:
-
Verify the token in
argocd-notifications-secretmatches your Backstage config. -
Check the Authorization header format:
Bearer $backstage-token. Restart the notifications controller after secret changes:
kubectl rollout restart deployment -n openshift-gitops -l app.kubernetes.io/name=argocd-notifications-controller
3. Security Considerations
- Use strong tokens - Generate random tokens with sufficient entropy.
- Rotate tokens periodically - Update both Argo CD secret and Backstage config.
- Use HTTPS - Ensure Backstage is accessible over HTTPS in production.
- Network policies - Consider restricting which pods can reach Backstage.