Merge pull request #33545 from backstage/sennyeya/deployment-guide

docs: add deployment golden path guide
This commit is contained in:
Andre Wanlin
2026-05-08 14:01:59 -05:00
committed by GitHub
8 changed files with 701 additions and 0 deletions
+87
View File
@@ -0,0 +1,87 @@
---
id: docker
sidebar_label: 001 - Building the Docker image
title: Building the Docker image
description: How to build your Backstage app into a deployable Docker image
---
Audience: Developers and Admins
## Summary
Every production deployment of Backstage starts with a Docker image. The image
bundles both the frontend and backend into a single artifact that you can deploy
anywhere containers run.
By the end of this page, you will have a Docker image ready to push to a
container registry.
## What is in the Docker image?
When you created your app with `@backstage/create-app`, a `Dockerfile` was
generated at `packages/backend/Dockerfile`. The build process layers both the
frontend and backend into a single image:
1. The **backend** is compiled and bundled into `packages/backend/dist/`.
2. The **frontend** is built and served by the
`@backstage/plugin-app-backend` plugin, which is included in the backend
by default.
3. Production dependencies are installed, and the result is packaged into a
slim Node.js image.
The image runs as a non-root `node` user and sets `NODE_ENV=production`.
## Building the image
The recommended approach is a **host build**, where compilation happens on
your machine (or CI runner) and Docker only packages the result. This is faster
and produces better caching behavior.
From the root of your repository:
```shell
yarn install
yarn tsc
yarn build:backend
```
Then build the Docker image:
```shell
docker image build . -f packages/backend/Dockerfile --tag backstage
```
To verify it works locally:
```shell
docker run -it -p 7007:7007 backstage
```
You should see logs in your terminal and be able to open `http://localhost:7007`
in your browser.
:::tip Troubleshooting
If you run into build issues, two Docker flags can help:
- `--progress=plain` shows verbose output instead of folded log sections.
- `--no-cache` rebuilds all layers from scratch.
```shell
docker image build . -f packages/backend/Dockerfile --tag backstage --progress=plain --no-cache
```
:::
## Further reading
For a full multi-stage Docker build (where everything happens inside Docker)
or for deploying the frontend separately, see the
[Building a Docker image](../../deployment/docker.md) reference documentation.
## Next steps
Before deploying, you need to set up two production dependencies: a database and
an authentication provider.
- [Setting up a production database](./002-database.md)
+103
View File
@@ -0,0 +1,103 @@
---
id: database
sidebar_label: 002 - Database
title: Setting up a production database
description: How to configure PostgreSQL for your Backstage production deployment
---
Audience: Admins
## Summary
During local development, Backstage uses SQLite as a fast in-memory database.
SQLite does not persist data across restarts and is not designed for
multi-instance deployments, so you need a dedicated database for production.
By the end of this page, you will have PostgreSQL configured as your Backstage
database.
## Why PostgreSQL?
PostgreSQL is the recommended production database for Backstage. It handles
concurrent connections well, supports the query patterns that Backstage plugins
use, and is available as a managed service from every major cloud provider.
Some options for running PostgreSQL:
- **Managed services**: Amazon RDS, Google Cloud SQL, Azure Database for
PostgreSQL, or similar offerings.
- **Self-hosted**: Running PostgreSQL in a container or on a dedicated server.
- **In Kubernetes**: Deploying PostgreSQL alongside Backstage (useful for
getting started, but managed services are preferred for production).
## Configuring Backstage to use PostgreSQL
Open your `app-config.production.yaml` and add the database configuration:
```yaml title="app-config.production.yaml"
backend:
database:
client: pg
connection:
host: ${POSTGRES_HOST}
port: ${POSTGRES_PORT}
user: ${POSTGRES_USER}
password: ${POSTGRES_PASSWORD}
```
The `${...}` syntax references environment variables, which keeps secrets out
of your configuration files. Set these variables in your deployment environment.
:::caution
Avoid hardcoding database credentials in configuration files. Use environment
variables or a secrets manager provided by your deployment platform.
:::
### Optional: SSL connections
If your database provider requires SSL (most managed services do), add the
SSL configuration:
```yaml title="app-config.production.yaml"
backend:
database:
client: pg
connection:
host: ${POSTGRES_HOST}
port: ${POSTGRES_PORT}
user: ${POSTGRES_USER}
password: ${POSTGRES_PASSWORD}
ssl:
require: true
rejectUnauthorized: true
```
If your provider uses a custom certificate authority, you can reference the
CA certificate file:
```yaml
ssl:
ca:
$file: /path/to/ca/server.crt
```
## How Backstage uses the database
Each plugin gets its own isolated database schema. Migrations run automatically
when the backend starts, so you do not need to run any manual migration steps.
The database coordinates state and work distribution between instances, which
is what makes horizontal scaling possible later on.
## Further reading
For step-by-step PostgreSQL installation instructions and cloud-specific
setup guides, see the [Database configuration guide](../../getting-started/config/database.md).
## Next steps
With the database in place, the next step is to replace the guest login with
a real authentication provider.
- [Configuring authentication](./003-authentication.md)
@@ -0,0 +1,90 @@
---
id: authentication
sidebar_label: 003 - Authentication
title: Configuring authentication
description: How to set up a production authentication provider for Backstage
---
Audience: Developers and Admins
## Summary
By default, Backstage uses a guest authentication provider that lets anyone
log in without credentials. This is convenient for local development but
creates a security risk when deployed to production. Before deploying, you
should configure a real authentication provider.
By the end of this page, you will understand what needs to change and where
to find the setup instructions for your chosen provider.
## Why replace guest authentication?
The guest provider is explicitly not intended for containerized or production
environments. With guest auth enabled, any user who can reach your Backstage
instance shares the same identity and has the same level of access. Replacing
it is one of the most important steps before going to production.
:::danger
Deploying Backstage with the guest authentication provider exposes your
instance to unauthorized access. Configure a real provider before deploying.
:::
## Choosing an authentication provider
Backstage supports a wide range of authentication providers. GitHub is a
common choice since most developers already have accounts, but you should
pick whatever your organization already uses for single sign-on.
Some popular options:
| Provider | Good fit when... |
| :------------------- | :------------------------------------------------ |
| GitHub | Your team uses GitHub and you want a quick setup. |
| Microsoft / Azure AD | Your company uses Microsoft Entra ID (Azure AD). |
| Google | Your company uses Google Workspace. |
| Okta | You use Okta as your identity provider. |
| OIDC | You have a generic OpenID Connect provider. |
| OAuth2 Proxy | You already use an authenticating reverse proxy. |
The full list of providers and their configuration is available in the
[Authentication documentation](../../auth/index.md).
## What the setup involves
Regardless of which provider you choose, the setup follows the same pattern:
1. **Create an OAuth application** (or equivalent) with your identity
provider. You will get a client ID and client secret.
2. **Add the provider configuration** to `app-config.yaml` with the
credentials, typically using environment variables for secrets.
3. **Install the backend auth module** for your chosen provider in
`packages/backend`.
4. **Configure a sign-in resolver** that maps the authenticated user identity
to a Backstage catalog user entity.
5. **Update the frontend** to show the correct sign-in page.
The [Authentication getting started guide](../../getting-started/config/authentication.md)
walks through this process step by step using GitHub as the example provider.
## Production configuration
Once authentication is configured, make sure that the guest provider is
disabled in your production config:
```yaml title="app-config.production.yaml"
auth:
providers:
guest: null
```
This ensures that even if the guest provider is configured in your base
`app-config.yaml` for local development, it is explicitly disabled in
production.
## Next steps
With both a database and authentication configured, you are ready to deploy.
- [Deploying to production](./004-deploying.md)
@@ -0,0 +1,110 @@
---
id: deploying
sidebar_label: 004 - Deploying
title: Deploying to production
description: How to deploy your Backstage instance to production
---
Audience: Admins
## Summary
You have a Docker image, a database, and authentication configured. Now it
is time to deploy. The _best_ way to deploy Backstage is _the same way_ you
deploy other software at your organization. Backstage is designed to run as
a stateless Node.js application backed by an external PostgreSQL database,
so it fits into most existing deployment pipelines without special tooling.
This page describes what every Backstage deployment needs, regardless of
platform, and points to the reference guides for specific targets.
## What every deployment needs
Whichever platform you choose, the deployment will need to take care of the
following concerns:
- **A container image** built from your repository and pushed to a registry
your runtime can pull from. The image you built in
[Building the Docker image](./001-docker.md) is the artifact that gets
deployed.
- **Configuration and secrets** delivered to the running container as
environment variables or mounted files. This includes database
credentials, auth provider client secrets, and any integration tokens.
- **A reachable PostgreSQL database** that the running instance can connect
to using the credentials from the previous step. See
[Configuring the database](./002-database.md) for details.
- **A network entry point** — typically an ingress, load balancer, or
reverse proxy — that exposes the backend on port `7007` to your users
over HTTPS.
- **A health-checked runtime** that can restart the container if it stops
responding and roll out new versions when you publish a new image.
- **`app.baseUrl` and `backend.baseUrl`** in your
`app-config.production.yaml` set to the public URL where users will
access Backstage. Auth providers and the frontend rely on these matching
the actual entry point:
```yaml title="app-config.production.yaml"
app:
baseUrl: https://backstage.example.com
backend:
baseUrl: https://backstage.example.com
listen:
port: 7007
```
## Choosing a deployment target
Backstage runs anywhere a Node.js container can run. Pick the option that
matches what your organization already operates — you do not need to adopt
new infrastructure to run Backstage.
| Target | Good fit when... |
| :----------------------- | :---------------------------------------------------------------------- |
| Kubernetes | Your organization already runs services on Kubernetes. |
| Amazon ECS / Fargate | You are on AWS and prefer managed container scheduling. |
| Google Cloud Run | You want a fully managed, request-driven container runtime on GCP. |
| Azure Container Apps | You are on Azure and want a managed container platform. |
| A traditional VM or PaaS | You prefer running the Node.js process directly behind a reverse proxy. |
| Docker Compose | You are running a small installation or proof of concept. |
Backstage maintains a reference guide for the Kubernetes path in
[Deploying with Kubernetes](../../deployment/k8s.md), which walks through
namespaces, secrets, the deployment, the service, and connecting to
PostgreSQL inside the cluster.
For other platforms, the
[community-contributed deployment guides](https://github.com/backstage/backstage/blob/master/contrib/docs/tutorials/)
in the Backstage repository cover targets such as ECS, Cloud Run, and
Docker Compose, and the [deployment overview](../../deployment/index.md)
explains the underlying model that all of these guides share.
## Operational concerns
A few operational details apply to every deployment and are worth getting
right before opening Backstage up to your users:
- **Run multiple replicas** behind your load balancer. Backstage is
stateless, so multiple instances can serve traffic against the same
PostgreSQL database. We cover this further in
[Scaling Backstage](./007-scaling.md).
- **Store secrets securely.** Container platforms typically offer a
secrets primitive — Kubernetes Secrets, AWS Secrets Manager, GCP Secret
Manager, Azure Key Vault, and so on. Reference these from your
configuration with environment variables rather than committing
credentials.
- **Enable health checks.** Wire your platform's readiness and liveness
probes to the Backstage health endpoints so unhealthy instances are
taken out of rotation and restarted automatically.
- **Run behind HTTPS.** Terminate TLS at your ingress, load balancer, or
reverse proxy and make sure the public URL is what `app.baseUrl` and
`backend.baseUrl` are set to.
If you need to run Backstage behind a corporate proxy, see the
[corporate proxy guide](../../tutorials/corporate-proxy.md).
## Next steps
Your Backstage instance is deployed. Next, let's look at how to manage
configuration effectively across environments.
- [Config-first development](./005-config-first.md)
@@ -0,0 +1,92 @@
---
id: config-first
sidebar_label: 005 - Configuration management
title: Config-first development
description: Managing Backstage configuration across environments
---
Audience: Developers and Admins
## Summary
One of the things that makes Backstage easier to operate over time is treating
configuration as the primary way to control application behavior. Instead of
writing custom code for every change, many behaviors can be toggled, adjusted,
or extended through configuration files.
By the end of this page, you will understand how Backstage configuration
layering works and how to manage it across environments.
## How configuration layering works
Backstage loads configuration from multiple `app-config*.yaml` files and
merges them together. Files loaded later override values from earlier files.
The Docker image built in the [first step](./001-docker.md) starts with this
command:
```
node packages/backend --config app-config.yaml --config app-config.production.yaml
```
This means `app-config.production.yaml` overrides any values set in
`app-config.yaml`. You can use this pattern to keep your base config for
local development and override only what changes in production.
### Common configuration split
| File | Purpose |
| :--------------------------- | :------------------------------------------------- |
| `app-config.yaml` | Base configuration shared across all environments. |
| `app-config.local.yaml` | Local overrides, not committed to source control. |
| `app-config.production.yaml` | Production-specific overrides. |
## Environment variables in config
Use the `${VAR_NAME}` syntax to reference environment variables. This is
the recommended approach for secrets and values that differ between
environments:
```yaml title="app-config.production.yaml"
backend:
database:
client: pg
connection:
host: ${POSTGRES_HOST}
port: ${POSTGRES_PORT}
user: ${POSTGRES_USER}
password: ${POSTGRES_PASSWORD}
```
This keeps secrets out of your repository and lets the same Docker image be
used across environments by changing the variables at deploy time.
## What to configure vs. what to code
A good rule of thumb: if a change affects _where_ something connects, _how_
it authenticates, or _which_ features are enabled, it belongs in configuration.
If it changes _what_ a feature does, it belongs in code.
Examples of config-driven behavior:
- Database connection details.
- Authentication provider selection and credentials.
- Catalog locations and entity providers.
- Integration tokens (GitHub, GitLab, etc.).
- TechDocs storage backend (local, S3, GCS, Azure).
- Proxy endpoints for external services.
Making configuration your primary lever for environment differences simplifies
your CI/CD pipeline. You build one Docker image and deploy it everywhere,
varying only the config files and environment variables.
## Further reading
For the full configuration reference, see the
[Configuration documentation](../../conf/index.md).
## Next steps
With your deployment running and configuration managed, you should set up
monitoring to keep it healthy.
- [Monitoring your deployment](./006-monitoring.md)
@@ -0,0 +1,85 @@
---
id: monitoring
sidebar_label: 006 - Monitoring
title: Monitoring your deployment
description: Setting up OpenTelemetry and frontend analytics for your Backstage deployment
---
Audience: Admins
## Summary
A production Backstage deployment needs monitoring to track health, diagnose
issues, and understand usage patterns. Backstage provides built-in support
for OpenTelemetry on the backend and an Analytics API on the frontend.
By the end of this page, you will know how to set up both.
## Backend monitoring with OpenTelemetry
Backstage uses [OpenTelemetry](https://opentelemetry.io/) to report metrics
and traces. The setup involves installing a few OpenTelemetry packages,
creating an instrumentation file, and loading it before the backend starts.
Follow the [Setup OpenTelemetry tutorial](../../tutorials/setup-opentelemetry.md)
for step-by-step instructions on installing dependencies, configuring
exporters (Prometheus for metrics, OTLP for traces), and wiring everything
into your backend.
### Key metrics to monitor
Backstage plugins emit metrics that give you insight into system health.
Common examples include:
- `catalog_entities_count` - Total number of entities in the catalog.
- `catalog.processed.entities.count` - Number of entities processed.
- `catalog.processing.duration` - Time spent processing entities.
- `scaffolder.task.count` - Number of scaffolder tasks run.
- `scaffolder.task.duration` - Time taken by scaffolder tasks.
The specific metric names may vary depending on which plugins you have
installed and their versions. These examples help you set up alerts for
things like processing backlogs or unusually slow scaffolder runs.
### Health checks
Backstage provides built-in health check endpoints that you can use for
liveness and readiness probes in Kubernetes or other orchestrators:
- `/.backstage/health/v1/readiness` - Returns healthy when the backend is
ready to serve traffic.
- `/.backstage/health/v1/liveness` - Returns healthy when the backend
process is alive.
## Frontend analytics
Backstage provides an Analytics API for tracking user behavior on the
frontend. This is useful for understanding which plugins get the most use
and measuring the return on your Backstage investment.
Several analytics tools are supported through community plugins:
- Google Analytics 4
- New Relic Browser
- Matomo
See the [Analytics documentation](../../frontend-system/building-plugins/08-analytics.md)
for setup instructions.
For frontend error reporting, consider integrating with a service like
Sentry, CloudWatch RUM, or Cloudflare RUM to catch and diagnose client-side
errors.
## Logging
The backend emits structured JSON logs on stdout by default. These logs
include fields like `service`, `plugin`, `level`, and `message` that make
them easy to parse with log aggregation tools (Elasticsearch, Datadog,
Splunk, etc.).
## Next steps
As more users adopt your Backstage instance, you may need to scale the
deployment.
- [Scaling your deployment](./007-scaling.md)
@@ -0,0 +1,96 @@
---
id: scaling
sidebar_label: 007 - Scaling
title: Scaling your deployment
description: How to scale Backstage as usage grows
---
Audience: Admins
## Summary
A single Backstage instance handles many users well, but as your organization
grows and more plugins are added, you may need to scale. This page covers the
strategies available.
## Horizontal scaling
The most straightforward approach is to run multiple identical instances of
Backstage behind a load balancer. All instances share the same external
database (and optional cache or search services). The backend plugins
coordinate through the database to share state and distribute work.
In Kubernetes, this is as simple as increasing the replica count in your
deployment:
```yaml
spec:
replicas: 3
```
No additional configuration is needed. The database handles coordination
between instances.
## Splitting the backend
For larger installations, you can break the backend into multiple services,
each running a different set of plugins. For example, you might run the
catalog and scaffolder as separate deployments so that heavy catalog
processing does not affect scaffolder performance.
This is a more advanced approach that requires:
- Separate backend packages, each importing only the plugins they need.
- A custom `DiscoveryService` implementation that routes requests to the
correct backend based on the plugin ID.
- Routing both external (ingress) and internal (backend-to-backend) traffic
appropriately.
See the
[backend system documentation](../../backend-system/building-backends/01-index.md#split-into-multiple-backends)
for details on how to set this up.
## Separating the frontend
By default, the frontend is served from your backend deployment using the
`@backstage/plugin-app-backend` plugin. If you need to reduce load on the
backend or serve the frontend from a CDN for better performance, you can
deploy the frontend separately.
This involves:
1. Removing the `@backstage/plugin-app-backend` plugin from the backend.
2. Building the frontend as a static bundle.
3. Serving it from a separate container (for example, NGINX) or a static
hosting provider.
An example NGINX setup is available in the
[contrib/docker/frontend-with-nginx](https://github.com/backstage/backstage/blob/master/contrib/docker/frontend-with-nginx)
folder.
:::note
When serving the frontend separately, configuration is no longer injected by
the backend at runtime. You need to provide the correct configuration at
frontend build time.
:::
## When to scale
Here are some signals that indicate you should consider scaling:
- API response times are increasing.
- Catalog processing is falling behind (visible in the
`catalog.processing.duration` metric).
- Scaffolder tasks are queuing for longer than expected.
- Users report slow page loads.
Start with horizontal scaling (more replicas) before considering backend
splitting. It is simpler and handles most growth scenarios.
## Further reading
For more details on scaling strategies, see the
[Scaling Backstage Deployments](../../deployment/scaling.md) reference
documentation.
+38
View File
@@ -0,0 +1,38 @@
---
id: index
title: Deploying Backstage to production
description: A guided path for deploying your Backstage app to production
---
## Prerequisites
- You have completed the [create-app golden path](../create-app/index.md) and
have a working Backstage app.
- Your code is pushed to a source control management system (GitHub, GitLab,
etc.).
- You have a general understanding of how your company builds and deploys
software.
## What should I get out of this guide?
This guide walks through everything you need to get your Backstage instance
running in a production environment. By the end, you will have:
- A Docker image containing your Backstage app.
- A production database (PostgreSQL) connected to your deployment.
- A real authentication provider replacing the default guest login.
- A running deployment, whether on Kubernetes, ECS, or another platform.
- Monitoring and observability set up with OpenTelemetry.
- An understanding of how to scale your deployment as usage grows.
## Structure
We start with the Docker image since that is the foundation of any deployment.
Then we set up the two critical pre-deploy dependencies: a database and
authentication. After that, we walk through deploying to Kubernetes and discuss
other deployment options. Finally, we cover operational topics like
configuration management, monitoring, and scaling.
## Next steps
- [Building the Docker image](./001-docker.md)