This project builds an Internal Developer Portal (IDP) using Backstage — the open-source platform engineering tool originally created by Spotify and now used by Atlassian, Netflix, Airbnb, and CRED. An IDP is the single place where every developer in your organisation goes to understand what services exist, who owns them, how to create new ones, and how to access documentation and runbooks. Without an IDP, onboarding a new engineer means a 2-week scavenger hunt — finding the right Slack channel to ask about a service, discovering that the documentation is 18 months out of date, and spending days understanding the CI/CD setup. With Backstage, everything is in one place. Developer | v +------------------+ | Backstage IDP | <- Single portal for everything | | | Software Catalog| <- What services exist and who owns them | Tech Docs | <- Documentation for every service | Templates | <- Golden paths to create new services | CI/CD View | <- Pipeline status from GitHub Actions +------------------+ / | \ \ / | \ \ GitHub PagerDuty Kubernetes Datadog (repos) (incidents) (cluster) (metrics)
As an engineering team grows beyond 10 people, two problems appear simultaneously. First, nobody knows what services exist or who owns them — the payment service, the notification service, the auth service, each built by a different team, each with different deployment processes and documentation styles. Second, creating a new service is a multi-day process of copying configuration from an existing service, setting up CI/CD from scratch, and asking three different people how to do things correctly. Backstage solves both. The Software Catalog gives every service a home page with owner, documentation, deployment status, and incident history. Golden Path Templates let a developer create a new production-ready service in 5 minutes — the template scaffolds the repository, sets up CI/CD, registers the service in the catalog, and creates the initial documentation structure.
### Step 1: Create a Backstage Application Backstage is a Node.js application that you own and customise. You create your own instance from the official template and deploy it. ```bash ## Prerequisites node --version # Needs Node.js 18 or 20 npm --version # Needs npm 8+ yarn --version # Backstage uses Yarn ## If yarn is not installed npm install -g yarn ## Create a new Backstage application npx @backstage/create-app@latest ## When prompted: ## Enter a name for your app: devops-network-portal ## This takes 3-5 minutes to set up cd devops-network-portal ## Start the development server to verify it works yarn dev ## Opens at http://localhost:3000 ## You should see the Backstage interface with the example catalog ``` > 📌 **Remember:** Backstage is YOUR application — you own the code and deploy it yourself. Think of it like a Next.js app that you customise. The Backstage team provides the framework and plugins, you provide the configuration and your company's specific integrations. ### Step 2: Configure the Software Catalog The Software Catalog is the heart of Backstage. Every service, website, library, and data pipeline in your organisation gets a `catalog-info.yaml` file at the root of its repository. Backstage reads these files and builds the catalog. Create a `catalog-info.yaml` for each of your existing services: ```yaml ## catalog-info.yaml — add this to each service repository apiVersion: backstage.io/v1alpha1 kind: Component metadata: name: order-service description: Handles order creation, tracking, and lifecycle for the platform annotations: # Connect to GitHub for repository info github.com/project-slug: devops-network/order-service # Connect to GitHub Actions for CI/CD status github.com/actions-workflow: deploy.yml # Documentation location backstage.io/techdocs-ref: dir:. tags: * nodejs * api * orders links: * url: https://grafana.devopsnetwork.in/d/order-service title: Grafana Dashboard icon: dashboard * url: https://runbooks.devopsnetwork.in/order-service title: Runbook icon: help spec: type: service lifecycle: production # or: experimental, deprecated owner: group:platform-team # Team that owns this service system: order-management dependsOn: * component:payment-service * component:notification-service providesApis: * order-api ``` **Configure Backstage to discover these catalog files from GitHub:** Edit `app-config.yaml` in your Backstage application: ```yaml ## app-config.yaml (in your Backstage app root) catalog: providers: github: devops-network-org: organization: 'your-github-org' # Your GitHub organization name catalogPath: '/catalog-info.yaml' # Where to look in each repo filters: branch: 'main' repository: '.*' # Match all repositories schedule: frequency: { minutes: 30 } # Scan GitHub every 30 minutes timeout: { minutes: 3 } integrations: github: * host: github.com token: ${GITHUB_TOKEN} # Set this as an environment variable ``` ### Step 3: Create a Golden Path Template A Golden Path Template is a Software Template in Backstage that scaffolds a complete new service. When a developer clicks CREATE, they fill in a form with the service name and owner, and Backstage automatically creates the GitHub repository, sets up the directory structure, configures CI/CD, and registers the service in the catalog. Create `templates/nodejs-microservice/template.yaml`: ```yaml apiVersion: scaffolder.backstage.io/v1beta3 kind: Template metadata: name: nodejs-microservice title: Node.js Microservice description: Production-ready Node.js microservice with Docker, CI/CD, and Kubernetes manifests tags: * nodejs * microservice * recommended spec: owner: group:platform-team type: service parameters: * title: Service Details required: * name * description * owner properties: name: title: Service Name type: string description: Name of the new microservice (e.g., inventory-service) pattern: '^[a-z][a-z0-9-]*$' # Enforce lowercase with hyphens description: title: Description type: string description: What does this service do? owner: title: Owner Team type: string description: The team that owns this service ui:field: OwnerPicker ui:options: allowedKinds: * Group environment: title: Target Environment type: string description: Which environment to deploy to first default: staging enum: * staging * production steps: # Step 1: Fetch the template skeleton * id: fetch-base name: Fetch Base Template action: fetch:template input: url: ./skeleton # The template files live in ./skeleton directory values: name: ${{ parameters.name }} description: ${{ parameters.description }} owner: ${{ parameters.owner }} # Step 2: Create the GitHub repository * id: publish name: Publish to GitHub action: publish:github input: allowedHosts: ['github.com'] description: ${{ parameters.description }} repoUrl: github.com?owner=devops-network&repo=${{ parameters.name }} defaultBranch: main repoVisibility: private # Step 3: Register the service in the Software Catalog * id: register name: Register in Catalog action: catalog:register input: repoContentsUrl: ${{ steps['publish'].output.repoContentsUrl }} catalogInfoPath: '/catalog-info.yaml' output: links: * title: Repository url: ${{ steps['publish'].output.remoteUrl }} * title: Open in Catalog icon: catalog entityRef: ${{ steps['register'].output.entityRef }} ``` **Create the template skeleton files** in `templates/nodejs-microservice/skeleton/`: ``` skeleton/ +-- catalog-info.yaml # Auto-registered catalog entry +-- Dockerfile # Production-ready Dockerfile +-- package.json # Node.js dependencies +-- src/ | +-- index.js # Express server boilerplate +-- k8s/ | +-- deployment.yaml # Kubernetes deployment | +-- service.yaml # Kubernetes service | +-- ingress.yaml # Kubernetes ingress +-- .github/ | +-- workflows/ | +-- deploy.yml # GitHub Actions CI/CD pipeline +-- docs/ +-- index.md # TechDocs starting point ``` All files in the skeleton use template variables like `${{ values.name }}` that get replaced when the template runs. ### Step 4: Enable TechDocs for Service Documentation TechDocs lets every service have its own documentation site generated from Markdown files in the repository. Documentation lives next to code — when the code changes, developers update the docs in the same PR. ```bash ## Install the TechDocs CLI npm install -g @techdocs/cli ## In each service repository, create a docs folder mkdir docs ## Create the documentation entry point cat > docs/index.md << 'EOF' ## Order Service The Order Service handles the complete lifecycle of customer orders on the platform. ## Architecture This service is a Node.js Express application that: * Receives order creation requests from the API gateway * Validates payment with the Payment Service * Sends confirmation via the Notification Service * Stores order state in PostgreSQL ## Running Locally ```bash npm install npm run dev ``` ## Deployment Deployments are handled automatically by GitHub Actions on every merge to `main`. See the [CI/CD pipeline](../.github/workflows/deploy.yml) for details. ## On-Call Runbook If this service is paging: 1. Check Grafana dashboard for error rate spike 2. Check PostgreSQL connection pool saturation 3. Check Payment Service health (common upstream cause) 4. Escalate to platform-team Slack channel if not resolved in 15 minutes EOF ## Create mkdocs.yml configuration cat > mkdocs.yml << 'EOF' site_name: Order Service Documentation docs_dir: docs nav: * Home: index.md plugins: * techdocs-core EOF ``` In `app-config.yaml`, configure TechDocs storage: ```yaml techdocs: builder: 'local' # Use 'external' and S3 for production generator: runIn: 'local' publisher: type: 'local' local: publishDirectory: '/tmp/techdocs' ``` ### Step 5: Deploy Backstage on Kubernetes ```bash ## Build the Backstage Docker image yarn build:all docker build -t backstage:latest . ## Push to your container registry docker tag backstage:latest YOUR_ECR_REGISTRY/backstage:latest docker push YOUR_ECR_REGISTRY/backstage:latest ## Create the Kubernetes manifests kubectl create namespace backstage ## Create secret with required environment variables kubectl create secret generic backstage-secrets \ --namespace backstage \ --from-literal=GITHUB_TOKEN=ghp_YOUR_GITHUB_TOKEN \ --from-literal=POSTGRES_PASSWORD=your_db_password ## Deploy Backstage kubectl apply -n backstage -f - <<EOF apiVersion: apps/v1 kind: Deployment metadata: name: backstage spec: replicas: 1 # Backstage is not stateless — use 1 replica with shared DB selector: matchLabels: app: backstage template: metadata: labels: app: backstage spec: containers: * name: backstage image: YOUR_ECR_REGISTRY/backstage:latest ports: * containerPort: 7007 envFrom: * secretRef: name: backstage-secrets resources: requests: memory: "512Mi" cpu: "250m" limits: memory: "1Gi" cpu: "1" --- apiVersion: v1 kind: Service metadata: name: backstage spec: selector: app: backstage ports: * port: 80 targetPort: 7007 type: LoadBalancer EOF kubectl get svc -n backstage ## Note the EXTERNAL-IP — this is your IDP URL ```
```bash ## 1. Access the Backstage UI BACKSTAGE_URL=$(kubectl get svc backstage -n backstage \ -o jsonpath='{.status.loadBalancer.ingress[0].hostname}') echo "Backstage available at: http://$BACKSTAGE_URL" ## 2. Verify the Software Catalog is populated ## Navigate to Catalog -> filter by Kind: Component ## Expected: All services with catalog-info.yaml appear ## 3. Test the Golden Path Template ## Navigate to Create -> Node.js Microservice ## Fill in the form and submit ## Verify: GitHub repo created, catalog entry registered, docs page available ## 4. Verify TechDocs renders for a service ## Click on a service in the catalog -> Docs tab ## Expected: Rendered Markdown documentation from the repository ## 5. Check GitHub integration shows CI/CD status ## Click on a service -> CI/CD tab ## Expected: GitHub Actions workflow runs visible ## 6. Verify dependency graph ## Click on order-service -> Dependencies tab ## Expected: Shows payment-service and notification-service as dependencies ## 7. Test catalog auto-discovery ## Add catalog-info.yaml to a new GitHub repository ## Wait 30 minutes (or trigger manual refresh) ## Verify new service appears in catalog automatically echo "Internal Developer Portal fully operational" ```
This project builds an Internal Developer Portal (IDP) using Backstage — the open-source platform engineering tool origi...
As an engineering team grows beyond 10 people, two problems appear simultaneously. First, nobody knows what services exi...
Step 1: Create a Backstage Application Backstage is a Node.js application that you own and customise. You create your ow...
...
Aligns directly with DevOps, Site Reliability (SRE), and Platform Engineering job descriptions.