diff --git a/docs/golden-path/deployment/001-docker.md b/docs/golden-path/deployment/001-docker.md new file mode 100644 index 0000000000..80549e0021 --- /dev/null +++ b/docs/golden-path/deployment/001-docker.md @@ -0,0 +1,110 @@ +--- +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 --immutable +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. + +## Running the build in CI + +While you can run this build locally, you will typically run it as part of your +CI pipeline. A GitHub Actions workflow might look like this: + +```yaml +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 24 + - run: yarn install --immutable + - run: yarn tsc + - run: yarn build:backend + - run: docker image build . -f packages/backend/Dockerfile --tag backstage +``` + +After building, push the image to your container registry (ECR, GCR, Docker +Hub, etc.) so that your deployment platform can pull it. + +:::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) diff --git a/docs/golden-path/deployment/002-database.md b/docs/golden-path/deployment/002-database.md new file mode 100644 index 0000000000..db6fd76da1 --- /dev/null +++ b/docs/golden-path/deployment/002-database.md @@ -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) diff --git a/docs/golden-path/deployment/003-authentication.md b/docs/golden-path/deployment/003-authentication.md new file mode 100644 index 0000000000..110e2e914a --- /dev/null +++ b/docs/golden-path/deployment/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) diff --git a/docs/golden-path/deployment/004-deploying.md b/docs/golden-path/deployment/004-deploying.md new file mode 100644 index 0000000000..eb900bbcd5 --- /dev/null +++ b/docs/golden-path/deployment/004-deploying.md @@ -0,0 +1,186 @@ +--- +id: deploying +sidebar_label: 004 - Deploying +title: Deploying to production +description: How to deploy your Backstage instance to Kubernetes and other platforms +--- + +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. This page covers deploying to Kubernetes +as a concrete example and points to resources for other platforms. + +## Deploying to Kubernetes + +Kubernetes is the most common deployment target for Backstage. The deployment +involves a few Kubernetes objects: a namespace, secrets, a deployment, and a +service. + +### 1. Create a namespace + +```yaml +# kubernetes/namespace.yaml +apiVersion: v1 +kind: Namespace +metadata: + name: backstage +``` + +```shell +kubectl apply -f kubernetes/namespace.yaml +``` + +### 2. Create secrets + +Store your database credentials and any other secrets (API tokens, auth +client secrets) as Kubernetes Secrets: + +```yaml +# kubernetes/backstage-secrets.yaml +apiVersion: v1 +kind: Secret +metadata: + name: backstage-secrets + namespace: backstage +type: Opaque +data: + POSTGRES_USER: + POSTGRES_PASSWORD: +``` + +:::note + +Kubernetes secrets are base64-encoded but not encrypted. Enable +[Encryption at Rest](https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/) +for your cluster and consider tools like +[SealedSecrets](https://github.com/bitnami-labs/sealed-secrets) for storing +secrets in Git. + +::: + +```shell +kubectl apply -f kubernetes/backstage-secrets.yaml +``` + +### 3. Create the deployment + +```yaml +# kubernetes/backstage.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: backstage + namespace: backstage +spec: + replicas: 1 + selector: + matchLabels: + app: backstage + template: + metadata: + labels: + app: backstage + spec: + containers: + - name: backstage + image: /backstage: + imagePullPolicy: IfNotPresent + ports: + - name: http + containerPort: 7007 + envFrom: + - secretRef: + name: backstage-secrets + readinessProbe: + httpGet: + port: 7007 + path: /.backstage/health/v1/readiness + livenessProbe: + httpGet: + port: 7007 + path: /.backstage/health/v1/liveness +``` + +Replace `/backstage:` with the full image URL from your +container registry. + +```shell +kubectl apply -f kubernetes/backstage.yaml +``` + +### 4. Create a service + +```yaml +# kubernetes/backstage-service.yaml +apiVersion: v1 +kind: Service +metadata: + name: backstage + namespace: backstage +spec: + selector: + app: backstage + ports: + - name: http + port: 80 + targetPort: http +``` + +```shell +kubectl apply -f kubernetes/backstage-service.yaml +``` + +### 5. Expose the service + +The Service is not accessible outside the cluster by default. Use a Kubernetes +[Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/) +or an +[external load balancer](https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/) +to expose Backstage to your users. + +Make sure the `app.baseUrl` and `backend.baseUrl` values in your +`app-config.production.yaml` match the URL where users will access Backstage: + +```yaml title="app-config.production.yaml" +app: + baseUrl: https://backstage.example.com +backend: + baseUrl: https://backstage.example.com + listen: + port: 7007 +``` + +## Other deployment platforms + +Backstage is a standard Node.js application running in a Docker container, so +it works on any platform that supports containers: + +- **Amazon ECS**: Create a task definition referencing your Docker image, set + up an ECS service, and place it behind an Application Load Balancer. +- **Google Cloud Run**: Deploy the image as a Cloud Run service with the + appropriate environment variables. +- **Azure Container Apps**: Deploy the image and configure ingress through + the Azure portal or CLI. +- **Docker Compose**: Suitable for smaller installations. Run Backstage and + PostgreSQL together using a `docker-compose.yaml`. + +Community-contributed deployment guides are available in the +[contrib/docs/tutorials](https://github.com/backstage/backstage/blob/master/contrib/docs/tutorials/) +folder of the Backstage repository. + +## Further reading + +For a detailed guide including setting up PostgreSQL in Kubernetes, see +the [Deploying with Kubernetes](../../deployment/k8s.md) reference +documentation. + +## Next steps + +Your Backstage instance is deployed. Next, let's look at how to manage +configuration effectively. + +- [Config-first development](./005-config-first.md) diff --git a/docs/golden-path/deployment/005-config-first.md b/docs/golden-path/deployment/005-config-first.md new file mode 100644 index 0000000000..1725c3f610 --- /dev/null +++ b/docs/golden-path/deployment/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) diff --git a/docs/golden-path/deployment/006-monitoring.md b/docs/golden-path/deployment/006-monitoring.md new file mode 100644 index 0000000000..e082a74788 --- /dev/null +++ b/docs/golden-path/deployment/006-monitoring.md @@ -0,0 +1,83 @@ +--- +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: + +- `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. + +These metrics 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) diff --git a/docs/golden-path/deployment/007-scaling.md b/docs/golden-path/deployment/007-scaling.md new file mode 100644 index 0000000000..d82321df54 --- /dev/null +++ b/docs/golden-path/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. diff --git a/docs/golden-path/deployment/index.md b/docs/golden-path/deployment/index.md index e69de29bb2..77df39a986 100644 --- a/docs/golden-path/deployment/index.md +++ b/docs/golden-path/deployment/index.md @@ -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)