Chapter 3.1 - Building Your Internal Developer Portal with Backstage - A Comprehensive Guide

What We Talk About When We Talk About Developer Portals

At its core, platform engineering is about self-service. This makes an internal developer portal critical in this new paradigm, as it’s where all self-service functionalities and integrations are implemented.

Given the importance of a developer portal, before building one, we need to clarify our expectations. Typically, to maximize the benefits of platform engineering, a developer portal will include several key features:


Choosing a Tool: Backstage

Now that we understand what we truly want in a developer portal, let’s start building it.

A developer portal will have diverse functionalities, and the design/coding part itself is a massive project that not all teams can afford. This means we need a tool to quickly build the portal. Fortunately, such a tool already exists: Backstage.

Backstage is an open platform for building developer portals. It’s not a developer portal itself, but rather a tool for building one. Backstage includes:1

Backstage’s architecture is highly flexible: it has a frontend written in React/TypeScript and a backend written in Node.js. Its capabilities can be extended by adding plugins.

Additionally, Backstage (created by Spotify) is now hosted by the Cloud Native Computing Foundation (CNCF) as an incubating project. This means users get all the community support they need. There are also office hours you can join interactively every Thursday to learn exactly how the open-source platform can improve developer efficiency and experience.

Today, we’ll build a developer portal from scratch. After this tutorial, you’ll be able to use a template with a secure CI workflow to start a new service, check its CI status, and view its documentation, all in one place.


Building the Portal

Prerequisites

These are likely already familiar to most DevOps engineers:


Creating the Portal

Run the following command:

npx @backstage/create-app

This command will prompt you to choose the version. For this tutorial, select version 1.4.0.

The command will then ask for a name for your application. Enter a descriptive name, like “my-portal”, press Enter, and wait for the application to finish setting up.

Once the setup is complete, navigate into your newly created directory and run:

yarn dev

That’s it! Your Backstage developer portal is now up and running.

Take some time to explore the interface and get familiar with the basic layout of the software catalog, templates, and documentation sections.


GitHub Authentication and Integration

Since the developer portal will be responsible for bootstrapping new code repositories, it requires operational permissions with GitHub. This is why we need to set up GitHub authentication and integration.

Personal Access Token for Integration

While using a GitHub App is often the best way to set up integrations due to its granular permission settings, for this tutorial, we’ll use a Personal Access Token (PAT) for a quicker start.

  1. Open the GitHub token creation page to create your personal access token.

  2. Provide a descriptive name in the “Note” field to identify this token, and choose an expiration date.

    • If you’re unsure about the expiration, we suggest selecting 7 days. (We’re only testing locally, not running in production.)

    • For this tutorial, we will set the scope to the maximum to avoid issues with GitHub permissions. However, never do this in a production environment!

  3. Then, export the token as an environment variable:

    export GITHUB_TOKEN="your_personal_access_token_here"
    
  4. In your app-config.yaml file, change the integrations section to the following:

    integrations:
      github:
        - host: github.com
          token: ${GITHUB_TOKEN}
    

Creating a GitHub OAuth Application

  1. Visit https://github.com/settings/applications/new to create your OAuth application.

  2. The Homepage URL should point to the Backstage frontend. For this tutorial, it will be http://localhost:3000.

  3. The Authorization callback URL will point to the Auth backend, most likely http://localhost:7007/api/auth/github/handler/frame.

  4. Then, open your app-config.yaml file again and update the auth section to configure authentication:

    auth:
      environment: development
      providers:
        github:
          development:
            clientId: your_github_oauth_app_client_id
            clientSecret: your_github_oauth_app_client_secret
    

    Important Security Best Practice: Storing client secrets as hardcoded values in configuration files violates security best practices and should only be used for local development. For production use, you should adhere to the 12-factor app principles and read these values from environment variables.

After these configuration changes, restart your Backstage development server by running yarn dev.


Creating a Template

Next, let’s prepare a software template that can be used to instantly spin up new services. A template should include:

For this tutorial, we will use this template: https://github.com/IronCore864/backstage-test-template

The directory structure is relatively simple, with only two parts:

skeleton Folder

The skeleton folder contains all the templated files that will be generated when a new service is created using this template. Variables are formatted as $. If you’re familiar with Helm, YAML, or Go templates (or any templating tool), you’ll find this easy to read and understand.

It’s worth highlighting the catalog-info.yaml file, which is used by Backstage. This file must exist; otherwise, the created service cannot be registered as a component in the portal. In our template, we’ve built in some GitHub Actions workflows. One will test pull requests and pushes to the main branch, and another will scan the repository for hardcoded secrets using ggshield.

This approach allows us to embed all CI/CD best practices directly into the template. When others roll out new services, they’ll have everything they need for security features enabled by default.

template.yaml File

The template.yaml file defines how the template appears in the portal’s user interface and what actions it performs. This file can be long and seem overwhelming at first glance, but a closer look reveals it’s quite intuitive:

Example Parameters:

parameters:
  - id: service_name
    title: Service Name
    type: string
    description: Unique name of the service
    ui:autofocus: true
    ui:options:
      rows: 1
  - id: owner
    title: Owner
    type: string
    description: Owner of the service
    ui:field: OwnerPicker
    ui:options:
      allowedKinds: ["Group"]

Example Steps:

steps:
  - id: fetch-base
    name: Fetch Base
    action: fetch:template
    input:
      url: ./skeleton
      values:
        service_name: $
        owner: $
        github_token: $ # Ensure this is securely handled
        gitguardian_api_key: $ # Ensure this is securely handled

  - id: publish
    name: Publish
    action: publish:github
    input:
      repoUrl: github.com?owner=$&repo=$

  - id: register
    name: Register
    action: catalog:register
    input:
      catalogInfoUrl: $

From the file above, we can infer its specific definition:

  1. First, it requires two input parameters: service_name and owner.

  2. Then, it selects a repository location using an additional parameter (GITGUARDIAN_API_KEY for the CI pipeline).

  3. Next, it fetches the template, renders it, publishes it to GitHub, and registers it in our portal.

Registering the Template

Finally, add your template to the portal’s catalog so others can use it.

The simplest catalog configuration is to declaratively add locations pointing to YAML files with static locations. These locations will be added under catalog.locations in your app-config.yaml file.

catalog:
  locations:
    - type: url
      target: https://github.com/IronCore864/backstage-test-template/blob/main/template.yaml

The rule above allows us to add templates from the specified URL.

Remember to restart your yarn dev server after these changes.


Bringing Everything Together

Now that everything is set up, it’s time to see it in action.

Visit http://localhost:3000, then click the “Create” button and select our template:

Enter the necessary information. You’ll need to create a GitGuardian API key here: https://dashboard.gitguardian.com/api/personal-access-tokens.

Once everything is set up, click “Next,” and then observe the results:

You can view your newly created service in our catalog:

We also created and rendered the files:

Finally, let’s check the CI status:

It appears the pipelines have completed successfully. You can click on them to view more details, including detailed steps and logs. If you prefer to view them in your CI software (GitHub Actions, in this case), you can click the corresponding link to jump directly there. For your reference, this repository was created using the template above.

This means that regardless of the toolchain a team uses, team members don’t have to remember 10 different URLs for 10 different tools, nor do they need to keep 10 tabs open constantly. When they need any information, they only need to visit the developer portal, and that’s precisely the advantage of an internal developer portal.


Summary

In this article, we reviewed the characteristics of a developer portal, learned how to create our own using the open-source Backstage tool, configured our portal with GitHub, created a software template, and bootstrapped a service from it.

In real-world scenarios, a developer portal can be much more as integrations grow. Imagine if you deploy services in Kubernetes, use Argo CD for GitOps deployments, HashiCorp Vault for secret management, and all these integrations are within your portal. When you need to check deployment status, for example, to view actual resources in K8s, you don’t have to visit Vault, Argo CD, or the K8s Dashboard. You don’t even have to remember their URLs; just click directly on the developer portal.

It’s important to note that this tutorial is only for a quick local start. For production use, further considerations and adjustments are necessary based on your specific situation. For instance, we currently use static configuration, which means catalog information would be lost if the development server were restarted. To address this, you would need to configure Postgres for the portal. Another example is using yarn dev to start both the frontend and backend; in production, you might want to separate them, deploy them as containers in K8s, and create Ingress for them.

In the next part of this tutorial, we will explore the mechanisms of Backstage plugins and see how to extend its functionality to a higher level.