Skip to main content

platform-engineering

Build internal developer platforms (IDPs) with self-service infrastructure, golden paths, and developer portals using Backstage, Crossplane, and score.

Source facts

Repository
BagelHole/DevOps-Security-Agent-Skills
Last source activity
March 24, 2026 at 22:02
Detected SKILL.md language
English
Stars
1,154
Forks
164

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
platform-engineering
description
Build internal developer platforms (IDPs) with self-service infrastructure, golden paths, and developer portals using Backstage, Crossplane, and score.
license
MIT
metadata
{"author":"devops-skills","version":"1.0"}
# Platform Engineering Platform engineering is the discipline of building and maintaining internal developer platforms (IDPs) that enable self-service capabilities for software engineering teams. The goal is to reduce cognitive load, standardize infrastructure provisioning, and accelerate delivery while maintaining governance and security guardrails. --- ## 1. When to Use Adopt platform engineering practices when your organization experiences: - **Cognitive overload on dev teams** -- developers spend more time on infrastructure wiring than writing business logic. - **Inconsistent environments** -- every team provisions infrastructure differently, causing drift and outages. - **Slow onboarding** -- new engineers take weeks to get a working development environment. - **Repeated toil** -- the same Terraform/Helm/CI boilerplate is copy-pasted across dozens of repos. - **Compliance bottlenecks** -- security and ops reviews gate every deployment, slowing release cadence. - **Scale inflection points** -- you have 5+ teams and shared infrastructure concerns (networking, observability, secrets). Platform engineering is NOT about replacing ops with a portal. It is about encoding organizational standards into reusable, self-service abstractions that dev teams consume through golden paths. --- ## 2. Backstage Setup [Backstage](https://backstage.io) is the leading open-source developer portal framework, originally created at Spotify. ### Installation ```bash # Prerequisites: Node.js 18+, yarn 1.x npx @backstage/create-app@latest # Follow the prompts -- name your app, e.g., "internal-platform" cd internal-platform # Start the development server yarn dev ``` ### Production Docker Build ```dockerfile # Dockerfile for Backstage production image FROM node:18-bookworm-slim AS build WORKDIR /app COPY package.json yarn.lock ./ COPY packages/ packages/ COPY plugins/ plugins/ RUN yarn install --frozen-lockfile RUN yarn tsc RUN yarn build:backend FROM node:18-bookworm-slim WORKDIR /app COPY --from=build /app/packages/backend/dist/ ./ COPY --from=build /app/node_modules/ ./node_modules/ COPY app-config.yaml app-config.production.yaml ./ ENV NODE_ENV=production CMD ["node", "packages/backend", "--config", "app-config.production.yaml"] ``` ### Core app-config.yaml ```yaml # app-config.yaml app: title: Internal Developer Platform baseUrl: http://localhost:3000 organization: name: MyOrg backend: baseUrl: http://localhost:7007 listen: port: 7007 database: client: pg connection: host: ${POSTGRES_HOST} port: ${POSTGRES_PORT} user: ${POSTGRES_USER} password: ${POSTGRES_PASSWORD} integrations: github: - host: github.com token: ${GITHUB_TOKEN} catalog: import: entityFilename: catalog-info.yaml pullRequestBranchName: backstage-integration rules: - allow: [Component, System, API, Resource, Location, Template] locations: - type: url target: https://github.com/myorg/software-catalog/blob/main/catalog-info.yaml - type: url target: https://github.com/myorg/backstage-templates/blob/main/all-templates.yaml ``` --- ## 3. Crossplane for Self-Service Infrastructure Crossplane extends Kubernetes to provision and manage cloud infrastructure through declarative YAML. ### Install Crossplane ```bash # Add the Crossplane Helm repo helm repo add crossplane-stable https://charts.crossplane.io/stable helm repo update # Install Crossplane into its own namespace helm install crossplane crossplane-stable/crossplane \ --namespace crossplane-system \ --create-namespace \ --set args='{"--enable-composition-revisions"}' # Install the AWS provider kubectl apply -f - <<EOF apiVersion: pkg.crossplane.io/v1 kind: Provider metadata: name: provider-aws spec: package: xpkg.upbound.io/upbound/provider-family-aws:v1.1.0 EOF # Configure AWS credentials kubectl create secret generic aws-creds \ -n crossplane-system \ --from-file=creds=./aws-credentials.txt kubectl apply -f - <<EOF apiVersion: aws.upbound.io/v1beta1 kind: ProviderConfig metadata: name: default spec: credentials: source: Secret secretRef: namespace: crossplane-system name: aws-creds key: creds EOF ``` ### CompositeResourceDefinition (XRD) This defines a new platform API that developers consume without knowing the underlying cloud resources. ```yaml # xrd-application-database.yaml apiVersion: apiextensions.crossplane.io/v1 kind: CompositeResourceDefinition metadata: name: xapplicationdatabases.platform.myorg.io spec: group: platform.myorg.io names: kind: XApplicationDatabase plural: xapplicationdatabases claimNames: kind: ApplicationDatabase plural: applicationdatabases versions: - name: v1alpha1 served: true referenceable: true schema: openAPIV3Schema: type: object properties: spec: type: object properties: parameters: type: object properties: engine: type: string enum: ["postgres", "mysql"] default: "postgres" engineVersion: type: string default: "15" storageGB: type: integer minimum: 20 maximum: 500 default: 20 instanceSize: type: string enum: ["small", "medium", "large"] default: "small" environment: type: string enum: ["dev", "staging", "prod"] required: - environment ``` ### Composition (AWS RDS) ```yaml # composition-aws-database.yaml apiVersion: apiextensions.crossplane.io/v1 kind: Composition metadata: name: applicationdatabase-aws labels: provider: aws spec: compositeTypeRef: apiVersion: platform.myorg.io/v1alpha1 kind: XApplicationDatabase resources: - name: rds-instance base: apiVersion: rds.aws.upbound.io/v1beta1 kind: Instance spec: forProvider: region: us-east-1 allocatedStorage: 20 autoMinorVersionUpgrade: true backupRetentionPeriod: 7 dbName: appdb deletionProtection: false publiclyAccessible: false skipFinalSnapshot: true storageEncrypted: true storageType: gp3 vpcSecurityGroupIdSelector: matchLabels: platform.myorg.io/network: shared dbSubnetGroupNameSelector: matchLabels: platform.myorg.io/network: shared patches: - type: FromCompositeFieldPath fromFieldPath: spec.parameters.engine toFieldPath: spec.forProvider.engine - type: FromCompositeFieldPath fromFieldPath: spec.parameters.engineVersion toFieldPath: spec.forProvider.engineVersion - type: FromCompositeFieldPath fromFieldPath: spec.parameters.storageGB toFieldPath: spec.forProvider.allocatedStorage - type: CombineFromComposite combine: variables: - fromFieldPath: spec.parameters.instanceSize strategy: map map: small: db.t3.micro medium: db.t3.medium large: db.r6g.large toFieldPath: spec.forProvider.instanceClass - name: db-secret base: apiVersion: secretstores.aws.upbound.io/v1beta1 kind: Secret spec: forProvider: region: us-east-1 patches: - type: FromCompositeFieldPath fromFieldPath: metadata.name toFieldPath: spec.forProvider.name transforms: - type: string string: fmt: "platform/%s/db-credentials" ``` ### Developer Claim (what devs actually write) ```yaml # my-app-database.yaml apiVersion: platform.myorg.io/v1alpha1 kind: ApplicationDatabase metadata: name: orders-db namespace: team-commerce spec: parameters: engine: postgres engineVersion: "15" storageGB: 50 instanceSize: medium environment: staging ``` --- ## 4. Golden Paths Golden paths are opinionated, well-supported paths through your tech stack that teams can follow to ship quickly. ### Backstage Software Template ```yaml # template-nodejs-service.yaml apiVersion: scaffolder.backstage.io/v1beta3 kind: Template metadata: name: nodejs-service title: Node.js Microservice description: Create a production-ready Node.js service with CI/CD, monitoring, and Kubernetes manifests. tags: - recommended - nodejs spec: owner: platform-team type: service parameters: - title: Service Details required: - name - owner - system properties: name: title: Service Name type: string pattern: "^[a-z0-9-]+$" ui:autofocus: true description: title: Description type: string owner: title: Owner Team type: string ui:field: OwnerPicker ui:options: catalogFilter: kind: Group system: title: System type: string ui:field: EntityPicker ui:options: catalogFilter: kind: System - title: Infrastructure properties: database: title: Database type: string enum: ["none", "postgres", "mysql"] default: "none" cacheLayer: title: Cache type: string enum: ["none", "redis"] default: "none" environment: title: Target Environment type: string enum: ["dev", "staging", "prod"] default: "dev" steps: - id: fetch-template name: Fetch Skeleton action: fetch:template input: url: ./skeleton values: name: ${{ parameters.name }} owner: ${{ parameters.owner }} system: ${{ parameters.system }} description: ${{ parameters.description }} database: ${{ parameters.database }} cacheLayer: ${{ parameters.cacheLayer }} - id: publish name: Publish to GitHub action: publish:github input: allowedHosts: ["github.com"] repoUrl: github.com?owner=myorg&repo=${{ parameters.name }} repoVisibility: internal defaultBranch: main protectDefaultBranch: true requireCodeOwnerReviews: true - id: create-argocd-app name: Register with ArgoCD action: argocd:create-resources input: appName: ${{ parameters.name }} argoInstance: main namespace: ${{ parameters.name }} repoUrl: https://github.com/myorg/${{ parameters.name }} path: deploy/k8s - 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 Backstage icon: catalog entityRef: ${{ steps.register.output.entityRef }} ``` ### Cookiecutter Template Structure ``` golden-path-nodejs/ cookiecutter.json {{cookiecutter.service_name}}/ Dockerfile package.json tsconfig.json src/ index.ts health.ts deploy/ k8s/ deployment.yaml service.yaml ingress.yaml .github/ workflows/ ci.yaml deploy.yaml catalog-info.yaml ``` ```json // cookiecutter.json { "service_name": "my-service", "description": "A new microservice", "owner_team": "platform", "port": "3000", "database": ["none", "postgres", "mysql"], "node_version": "18" } ``` --- ## 5. Service Catalog ### catalog-info.yaml for a Microservice ```yaml # catalog-info.yaml apiVersion: backstage.io/v1alpha1 kind: Component metadata: name: orders-service description: Handles order creation, fulfillment, and tracking. annotations: backstage.io/techdocs-ref: dir:. github.com/project-slug: myorg/orders-service backstage.io/kubernetes-id: orders-service backstage.io/kubernetes-namespace: team-commerce argocd/app-name: orders-service pagerduty.com/integration-key: ${PAGERDUTY_KEY} grafana/dashboard-selector: "app=orders-service" tags: - nodejs - grpc links: - url: https://grafana.internal/d/orders-service title: Grafana Dashboard icon: dashboard - url: https://runbooks.internal/orders-service title: Runbook icon: docs spec: type: service lifecycle: production owner: team-commerce system: commerce-platform providesApis: - orders-api consumesApis: - inventory-api - payments-api dependsOn: - resource:orders-db - resource:orders-cache ``` ### API Entity ```yaml # orders-api.yaml apiVersion: backstage.io/v1alpha1 kind: API metadata: name: orders-api description: Order management API annotations: backstage.io/techdocs-ref: dir:. spec: type: openapi lifecycle: production owner: team-commerce system: commerce-platform definition: $text: ./api/openapi.yaml ``` ### TechDocs Configuration ```yaml # mkdocs.yml (in the service repo root) site_name: Orders Service site_description: Technical documentation for the Orders Service nav: - Home: index.md - Architecture: architecture.md - API Reference: api-reference.md - Runbook: runbook.md - ADRs: - adr/001-choose-grpc.md - adr/002-event-sourcing.md plugins: - techdocs-core markdown_extensions: - admonition - pymdownx.details - pymdownx.superfences: custom_fences: - name: mermaid class: mermaid format: !!python/name:pymdownx.superfences.fence_code_format ``` Enable TechDocs in `app-config.yaml`: ```yaml techdocs: builder: external generator: runIn: docker publisher: type: awsS3 awsS3: bucketName: myorg-techdocs region: us-east-1 credentials: roleArn: arn:aws:iam::123456789012:role/techdocs-publisher ``` --- ## 6. Developer Portal -- Backstage Plugins ### Kubernetes Plugin ```bash # Install Kubernetes plugin yarn --cwd packages/app add @backstage/plugin-kubernetes yarn --cwd packages/backend add @backstage/plugin-kubernetes-backend ``` Backend configuration in `app-config.yaml`: ```yaml kubernetes: serviceLocatorMethod: type: multiTenant
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub