Contents

Backstage Series #3: Integrate GitHub, ArgoCD, and Kubernetes in Your Portal

March 2026: This article uses Backstage v1.49+, ArgoCD v3.3.x, and the official plugins @roadiehq/backstage-plugin-argo-cd and @backstage/plugin-github-actions.

What You’ll Achieve

In the previous posts, we built the pieces separately: Backstage generating manifests, PRs on GitHub, and ArgoCD deploying to Kubernetes. Today we’re going to bring it all together inside the Backstage portal. By the end of this post, your portal will show:

  • GitHub Actions workflow status directly on each service.
  • ArgoCD sync status — whether your app is synced, degraded, or out of sync.
  • Kubernetes pods and resources in real time.
  • Everything connected: a developer fills a form, a PR is created, it gets approved, ArgoCD syncs, and you can see the entire cycle from Backstage.

Prerequisites

This is the third installment of the series. You need to have completed these posts:

  1. Backstage Series #1: Getting Started with Backstage — installation and first template.
  2. Backstage Series #2: Generate Kubernetes Manifests and Create PRs — Kubernetes plugin, Software Templates, and PRs.
  3. ArgoCD: GitOps on Your Kubernetes Cluster Step by Step — ArgoCD installation and first GitOps app.
ToolVersionVerify
Backstagev1.49+yarn backstage-cli info
ArgoCDv3.3+argocd version
Kindv0.20+kind version
kubectlv1.35+kubectl version --client
GitHub Tokenrepo, workflow permissionsConfigured in app-config.yaml

Verify that your cluster and ArgoCD are running:

# Cluster
kind get clusters

# ArgoCD
kubectl get pods -n argocd

Step 1: Integrate GitHub Actions in Backstage

Let’s start with the easiest one: seeing your CI/CD pipeline status directly in Backstage. The GitHub Actions plugin shows you workflows, their statuses, and logs — without leaving the portal.

Install the Plugin

# Frontend: adds the GitHub Actions tab on entities
yarn --cwd packages/app add @backstage/plugin-github-actions

Add the UI Tab

In packages/app/src/components/catalog/EntityPage.tsx:

import { EntityGithubActionsContent } from '@backstage/plugin-github-actions';

// Inside serviceEntityPage, add this route:
<EntityLayout.Route path="/ci-cd" title="CI/CD">
  <EntityGithubActionsContent />
</EntityLayout.Route>

Configure GitHub Authentication

If you already have the GitHub integration from Series #1 configured, this should already work. Just verify your app-config.yaml has this:

integrations:
  github:
    - host: github.com
      token: ${GITHUB_TOKEN}
Tip
The token needs the workflow scope in addition to repo to read GitHub Actions workflows. If your token doesn’t have it, generate a new one in GitHub → Settings → Developer settings → Personal access tokens.

Annotate Catalog Entities

For the plugin to know which workflows to show, your entities need the github.com/project-slug annotation:

apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: api-users
  annotations:
    github.com/project-slug: your-username/api-users
    backstage.io/kubernetes-id: api-users
    backstage.io/kubernetes-namespace: staging
spec:
  type: service
  lifecycle: production
  owner: platform-team

Restart Backstage and navigate to the entity. You should see the CI/CD tab with the latest workflow runs.


Step 2: Integrate ArgoCD in Backstage

Now for the good stuff. We’re going to give Backstage the ability to see your ArgoCD application status. The plugin shows you whether the app is synced, if there’s drift, sync history, and more.

Install the ArgoCD Plugin

We’ll use the Roadie plugin, which is the most mature and complete in the ecosystem:

# Frontend: ArgoCD components for entities
yarn --cwd packages/app add @roadiehq/backstage-plugin-argo-cd

# Backend: proxy for the ArgoCD API
yarn --cwd packages/backend add @roadiehq/backstage-plugin-argo-cd-backend

Register the Backend

In packages/backend/src/index.ts:

const backend = createBackend();

// ... other plugins ...

backend.add(import('@roadiehq/backstage-plugin-argo-cd-backend'));

backend.start();

Add the UI Components

In packages/app/src/components/catalog/EntityPage.tsx, you have two options. I recommend both:

Option 1: Overview card (shows a quick status summary):

import { EntityArgoCDOverviewCard } from '@roadiehq/backstage-plugin-argo-cd';

// Inside the serviceEntityPage overview:
<Grid item md={6}>
  <EntityArgoCDOverviewCard />
</Grid>

Option 2: Dedicated tab (shows full details):

import { EntityArgoCDHistoryCard } from '@roadiehq/backstage-plugin-argo-cd';

// As a route in serviceEntityPage:
<EntityLayout.Route path="/argocd" title="ArgoCD">
  <EntityArgoCDHistoryCard />
</EntityLayout.Route>

Step 3: Configure the ArgoCD Connection

Open your app-config.yaml and add the proxy and plugin configuration:

argocd:
  baseUrl: https://localhost:8080
  username: admin
  password: ${ARGOCD_ADMIN_PASSWORD}

proxy:
  endpoints:
    '/argocd/api':
      target: https://localhost:8080/api/v1/
      changeOrigin: true
      secure: false
      headers:
        Cookie:
          $env: ARGOCD_AUTH_TOKEN

Get the ArgoCD Credentials

If you followed the ArgoCD post, you already have ArgoCD running in your cluster. Let’s get what you need:

# Get the admin password
kubectl -n argocd get secret argocd-initial-admin-secret \
  -o jsonpath="{.data.password}" | base64 -d

# Port-forward to access the ArgoCD API
kubectl port-forward svc/argocd-server -n argocd 8080:443

Now you need an API token for the proxy. There are two ways to get it:

Option A: Using the ArgoCD CLI

# Login
argocd login localhost:8080 --insecure --username admin --password <YOUR_PASSWORD>

# Generate an API token
argocd account generate-token --account admin

Option B: Using the API directly

# Get the JWT token
ARGOCD_AUTH_TOKEN=$(curl -sk https://localhost:8080/api/v1/session \
  -d '{"username":"admin","password":"<YOUR_PASSWORD>"}' | jq -r '.token')

echo $ARGOCD_AUTH_TOKEN

Export the variables before starting Backstage:

export ARGOCD_ADMIN_PASSWORD="<your-password>"
export ARGOCD_AUTH_TOKEN="argocd.token=$ARGOCD_AUTH_TOKEN"
Security
In production, never use the admin account. Create a service user with read-only permissions in ArgoCD. For local development it’s fine, but keep this in mind when moving to a real environment.

Step 4: Annotate Entities with ArgoCD

For the ArgoCD plugin to know which application to show on each catalog entity, you need to add the argocd/app-name annotation:

apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: api-users
  annotations:
    github.com/project-slug: your-username/api-users
    backstage.io/kubernetes-id: api-users
    backstage.io/kubernetes-namespace: staging
    argocd/app-name: api-users
spec:
  type: service
  lifecycle: production
  owner: platform-team

Notice how the catalog-info.yaml now has four annotations connecting this entity to three different systems: GitHub, Kubernetes, and ArgoCD. That’s the power of Backstage — one entity, multiple views.

Multiple ArgoCD Apps

If your service has multiple ArgoCD applications (for example, one per environment), you can use a comma-separated list:

argocd/app-name: api-users-dev, api-users-staging, api-users-prod

Or use a label selector:

argocd/app-selector: app=api-users

Step 5: Create the ArgoCD Application

If you don’t have an ArgoCD application pointing to your manifests repo yet, let’s create one. This is what closes the loop with what we did in Series #2.

# argocd-app.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: api-users
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/your-username/k8s-configs.git
    targetRevision: main
    path: services/api-users/manifests
  destination:
    server: https://kubernetes.default.svc
    namespace: staging
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
      - CreateNamespace=true

Apply it:

kubectl apply -f argocd-app.yaml

With syncPolicy.automated enabled, ArgoCD will automatically sync whenever it detects changes in the repo. The selfHeal: true means that if someone modifies something directly in the cluster, ArgoCD will revert it to the Git state.


Step 6: Test the Full Flow

Now let’s close the loop end to end. This is the moment of truth:

1. Generate Manifests from Backstage

Go to Backstage → Create → Generate Kubernetes Deployment (the template from Series #2):

  • Service: api-orders
  • Namespace: staging
  • Image: nginx:1.27
  • Port: 80
  • Replicas: 2
  • HPA: enabled
  • Repository: github.com?repo=k8s-configs&owner=your-username

Backstage generates the manifests and creates the PR.

2. Review and Approve the PR on GitHub

Go to GitHub, review the generated manifests, and merge the PR. You can see the PR status directly from the CI/CD tab in Backstage if you have workflows configured.

3. Create the ArgoCD App

argocd app create api-orders \
  --repo https://github.com/your-username/k8s-configs.git \
  --path services/api-orders/manifests \
  --dest-server https://kubernetes.default.svc \
  --dest-namespace staging \
  --sync-policy automated \
  --auto-prune \
  --self-heal

4. Verify from Backstage

Register the entity in the catalog with all annotations:

apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: api-orders
  annotations:
    github.com/project-slug: your-username/k8s-configs
    backstage.io/kubernetes-id: api-orders
    backstage.io/kubernetes-namespace: staging
    argocd/app-name: api-orders
spec:
  type: service
  lifecycle: production
  owner: platform-team

Now navigate to api-orders in the Backstage catalog. You should see:

  • Overview: The ArgoCD card showing sync status (Synced / Healthy).
  • CI/CD: GitHub Actions workflows.
  • ArgoCD: Sync history with dates and revisions.
  • Kubernetes: Pods running in the staging namespace, with their status and logs.

Step 7: Bonus — Template That Creates Everything at Once

What if the Backstage template didn’t just create the PR with manifests, but also registered the entity in the catalog and created the ArgoCD application? Let’s extend the template from Series #2 to do exactly that.

Add these steps to template.yaml after the create-pr step:

    - id: create-catalog-entry
      name: Register in catalog
      action: catalog:register
      input:
        repoContentsUrl: ${{ steps['create-pr'].output.repoContentsUrl }}
        catalogInfoPath: /catalog-info.yaml

    - id: create-argocd-app
      name: Create ArgoCD application
      action: argocd:create-application
      input:
        appName: ${{ parameters.serviceName }}
        projectName: default
        repoURL: https://github.com/${{ (parameters.repoUrl | parseRepoUrl).owner }}/${{ (parameters.repoUrl | parseRepoUrl).repo }}.git
        path: services/${{ parameters.serviceName }}/manifests
        destServer: https://kubernetes.default.svc
        destNamespace: ${{ parameters.namespace }}

And add a catalog-info.yaml to the skeleton:

# skeleton/catalog-info.yaml
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: ${{ values.serviceName }}
  annotations:
    github.com/project-slug: ${{ values.repoOwner }}/${{ values.repoName }}
    backstage.io/kubernetes-id: ${{ values.serviceName }}
    backstage.io/kubernetes-namespace: ${{ values.namespace }}
    argocd/app-name: ${{ values.serviceName }}
spec:
  type: service
  lifecycle: production
  owner: platform-team
About the ArgoCD Action

The argocd:create-application action doesn’t come bundled with Backstage by default. You need to install the ArgoCD scaffolder module:

yarn --cwd packages/backend add @roadiehq/scaffolder-backend-argocd

And register it in the backend:

backend.add(import('@roadiehq/scaffolder-backend-argocd'));

Troubleshooting

ProblemSolution
ArgoCD card shows “Not Found”Verify that the argocd/app-name annotation matches the exact app name in ArgoCD
GitHub Actions won’t loadConfirm the token has the workflow scope and github.com/project-slug is correct
Kubernetes tab is emptyCheck that the backstage.io/kubernetes-id annotation matches the app label on your pods
ArgoCD proxy errorVerify port-forward is active: kubectl port-forward svc/argocd-server -n argocd 8080:443
ArgoCD shows “OutOfSync”This can be normal after a recent PR. Wait a few seconds or do a manual sync from ArgoCD’s UI

Summary

StepWhat We Did
1Installed the GitHub Actions plugin to see CI/CD in Backstage
2Installed the ArgoCD plugin (frontend + backend)
3Configured the proxy and credentials for ArgoCD
4Annotated catalog entities to connect all three systems
5Created an ArgoCD application pointing to the manifests repo
6Tested the full flow: form → PR → merge → sync → deploy
7Extended the template to automate registration and app creation

The Full Picture

With these three posts in the series, we built an end-to-end GitOps flow:

  1. Backstage is the front door. Developers don’t need to touch kubectl, write YAML by hand, or know the config repo structure.
  2. GitHub is where code and manifests live. PRs provide visibility and control over what gets deployed.
  3. ArgoCD is the GitOps engine. It ensures the cluster always reflects what’s in Git.
  4. Kubernetes is where services run. And thanks to the plugins, you can see their status without leaving Backstage.

This is Platform Engineering in action: giving developers a self-service experience that’s secure, auditable, and standardized. The platform team defines the templates and policies, and developers create and deploy services without depending on anyone.


Resources