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-cdand@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:
- Backstage Series #1: Getting Started with Backstage — installation and first template.
- Backstage Series #2: Generate Kubernetes Manifests and Create PRs — Kubernetes plugin, Software Templates, and PRs.
- ArgoCD: GitOps on Your Kubernetes Cluster Step by Step — ArgoCD installation and first GitOps app.
| Tool | Version | Verify |
|---|---|---|
| Backstage | v1.49+ | yarn backstage-cli info |
| ArgoCD | v3.3+ | argocd version |
| Kind | v0.20+ | kind version |
| kubectl | v1.35+ | kubectl version --client |
| GitHub Token | repo, workflow permissions | Configured in app-config.yaml |
Verify that your cluster and ArgoCD are running:
# Cluster
kind get clusters
# ArgoCD
kubectl get pods -n argocdStep 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-actionsAdd 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}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-teamRestart 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-backendRegister 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_TOKENGet 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:443Now 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 adminOption 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_TOKENExport the variables before starting Backstage:
export ARGOCD_ADMIN_PASSWORD="<your-password>"
export ARGOCD_AUTH_TOKEN="argocd.token=$ARGOCD_AUTH_TOKEN"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-teamNotice 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.
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-prodOr use a label selector:
argocd/app-selector: app=api-usersStep 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=trueApply it:
kubectl apply -f argocd-app.yamlWith 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-heal4. 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-teamNow 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
stagingnamespace, 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-teamThe 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-argocdAnd register it in the backend:
backend.add(import('@roadiehq/scaffolder-backend-argocd'));Troubleshooting
| Problem | Solution |
|---|---|
| ArgoCD card shows “Not Found” | Verify that the argocd/app-name annotation matches the exact app name in ArgoCD |
| GitHub Actions won’t load | Confirm the token has the workflow scope and github.com/project-slug is correct |
| Kubernetes tab is empty | Check that the backstage.io/kubernetes-id annotation matches the app label on your pods |
| ArgoCD proxy error | Verify 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
| Step | What We Did |
|---|---|
| 1 | Installed the GitHub Actions plugin to see CI/CD in Backstage |
| 2 | Installed the ArgoCD plugin (frontend + backend) |
| 3 | Configured the proxy and credentials for ArgoCD |
| 4 | Annotated catalog entities to connect all three systems |
| 5 | Created an ArgoCD application pointing to the manifests repo |
| 6 | Tested the full flow: form → PR → merge → sync → deploy |
| 7 | Extended 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:
- Backstage is the front door. Developers don’t need to touch
kubectl, write YAML by hand, or know the config repo structure. - GitHub is where code and manifests live. PRs provide visibility and control over what gets deployed.
- ArgoCD is the GitOps engine. It ensures the cluster always reflects what’s in Git.
- 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.