Red Hat Developer Hub 1.9

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

Red Hat Customer Content Services

Abstract

This document brings together documentation for emerging, evolving, and supplemental capabilities in Red Hat Developer Hub. It includes newly introduced features, technology, or developer previews, and supported functionality that might not yet be fully covered in the core documentation. Use this content to explore additional ways to extend, customize, and get more value from your Developer Hub environment. Because these capabilities are actively developing, the content might change as features mature and feedback is incorporated.

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.

Important

Administrative requirements: This integration requires infrastructure-level access. To proceed, you must have:

  • cluster-admin permissions 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: true

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

Security 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-cm ConfigMap. If you edit the ConfigMap directly, your changes may be overwritten. Instead, always use the NotificationsConfiguration Custom Resource (CR) as shown below.

Option A: For OpenShift GitOps

Use the NotificationsConfiguration Custom Resource:

Note

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 when expressions 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]
EOF

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

  1. The Backstage entity reference - tells Argo CD which Backstage entity should receive notifications.
  2. Trigger subscriptions - tells Argo CD which events to notify about.
Important

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.

  1. 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: production
  2. Argo 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: true
  3. What Happens

    1. Developer pushes code → Argo CD detects change and syncs.
    2. Sync succeeds → Argo CD notifications controller fires on-deployed-backstage trigger.
    3. Webhook sent → POST to Backstage /api/notifications/notifications.
    4. Notification appears → Team sees "🚀 order-service - Deployed" in Backstage with a link to Argo CD.

2. Troubleshooting

2.1. Notifications not appearing in RHDH

  1. Check notifications controller is running:

    kubectl get pods -n openshift-gitops | grep notification
  2. Check controller logs for errors:

    kubectl logs -n openshift-gitops -l app.kubernetes.io/name=argocd-notifications-controller --tail=50
  3. Check logs for backstage-related messages:

    kubectl logs -n openshift-gitops deployment/openshift-gitops-notifications-controller --tail=50 | grep -i backstage
  4. Verify webhook service is configured:

    kubectl get configmap argocd-notifications-cm -n openshift-gitops -o jsonpath='{.data.service\.webhook\.backstage}'
  5. 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:

  1. OpenShift GitOps: You edited the ConfigMap directly instead of using NotificationsConfiguration CR. The operator overwrites ConfigMap changes.
  2. Missing service configuration: The service.webhook.backstage key 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:

  1. Template name conflicts: Using names like template.app-sync-succeeded that conflict with Argo CD defaults (which use email/message format, not webhook).
  2. 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.5. Authentication Errors

If you see 401 Unauthorized or token errors:

  1. Verify the token in argocd-notifications-secret matches your Backstage config.
  2. Check the Authorization header format: Bearer $backstage-token.
  3. 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

  1. Use strong tokens - Generate random tokens with sufficient entropy.
  2. Rotate tokens periodically - Update both Argo CD secret and Backstage config.
  3. Use HTTPS - Ensure Backstage is accessible over HTTPS in production.
  4. Network policies - Consider restricting which pods can reach Backstage.

4. Additional Resources

Legal Notice

Copyright © 2026 Red Hat, Inc.
The text of and illustrations in this document are licensed by Red Hat under a Creative Commons Attribution–Share Alike 3.0 Unported license ("CC-BY-SA"). An explanation of CC-BY-SA is available at http://creativecommons.org/licenses/by-sa/3.0/. In accordance with CC-BY-SA, if you distribute this document or an adaptation of it, you must provide the URL for the original version.
Red Hat, as the licensor of this document, waives the right to enforce, and agrees not to assert, Section 4d of CC-BY-SA to the fullest extent permitted by applicable law.
Red Hat, Red Hat Enterprise Linux, the Shadowman logo, the Red Hat logo, JBoss, OpenShift, Fedora, the Infinity logo, and RHCE are trademarks of Red Hat, Inc., registered in the United States and other countries.
Linux® is the registered trademark of Linus Torvalds in the United States and other countries.
Java® is a registered trademark of Oracle and/or its affiliates.
XFS® is a trademark of Silicon Graphics International Corp. or its subsidiaries in the United States and/or other countries.
MySQL® is a registered trademark of MySQL AB in the United States, the European Union and other countries.
Node.js® is an official trademark of Joyent. Red Hat is not formally related to or endorsed by the official Joyent Node.js open source or commercial project.
The OpenStack® Word Mark and OpenStack logo are either registered trademarks/service marks or trademarks/service marks of the OpenStack Foundation, in the United States and other countries and are used with the OpenStack Foundation's permission. We are not affiliated with, endorsed or sponsored by the OpenStack Foundation, or the OpenStack community.
All other trademarks are the property of their respective owners.