---
id: devops-backstage-platform
title: Backstage / Internal Developer Portal
category: Coding
status: draft
source_trust_level: B
verification_status: conceptual
created_at: 2026-05-09
updated_at: 2026-05-09
tags: [devops, backstage, idp, platform, vibe-coding]
tech_stack: { language: "TS / React", applicable_to: ["DevOps"] }
applied_in: []
aliases: [Backstage, IDP, internal developer portal, service catalog, scaffolder, TechDocs]
---

# Backstage / IDP

> "어디 service 인지 / docs 어디 / oncall 누구" 통합 portal. **Spotify Backstage = OSS, CNCF**. Service catalog + scaffolder + TechDocs + plugins.

## 📖 핵심 개념
- Service catalog: 모든 service / lib / API.
- Scaffolder: template 기반 새 service 생성.
- TechDocs: docs as code.
- Plugins: 통합 (Argo, Jenkins, GitHub, AWS).

## 💻 코드 패턴

### Backstage 설치
```bash
npx @backstage/create-app@latest

cd my-portal
yarn dev
```

→ 자체 portal 실행.

### Service catalog (catalog-info.yaml)
```yaml
# 매 service repo 의 catalog-info.yaml
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: orders-service
  description: Handles customer orders
  annotations:
    github.com/project-slug: myorg/orders-service
    backstage.io/techdocs-ref: dir:.
    pagerduty.com/integration-key: abc123
    grafana/dashboard-selector: "tags @> 'orders'"
  tags:
    - typescript
    - backend
spec:
  type: service
  lifecycle: production
  owner: payments-team
  system: commerce
  providesApis:
    - orders-api
  consumesApis:
    - users-api
  dependsOn:
    - resource:postgresql-orders
    - component:notification-service
```

→ Backstage 가 자동 discovery.

### API
```yaml
apiVersion: backstage.io/v1alpha1
kind: API
metadata:
  name: orders-api
spec:
  type: openapi
  lifecycle: production
  owner: payments-team
  definition:
    $text: ./openapi.yaml
```

### Resource (DB, queue, etc)
```yaml
apiVersion: backstage.io/v1alpha1
kind: Resource
metadata:
  name: postgresql-orders
spec:
  type: database
  owner: platform-team
  system: commerce
```

### System / Domain
```yaml
apiVersion: backstage.io/v1alpha1
kind: System
metadata: { name: commerce }
spec:
  owner: payments-team
  domain: ecommerce

apiVersion: backstage.io/v1alpha1
kind: Domain
metadata: { name: ecommerce }
spec:
  owner: cto
```

### Owner (group)
```yaml
apiVersion: backstage.io/v1alpha1
kind: Group
metadata:
  name: payments-team
  description: Payments engineering team
spec:
  type: team
  profile:
    displayName: Payments Team
  parent: engineering
  children: []
  members:
    - alice
    - bob
```

### Scaffolder (template)
```yaml
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
  name: nodejs-service
  title: New Node.js Service
spec:
  owner: platform-team
  type: service
  
  parameters:
    - title: Service Info
      required: [name, owner]
      properties:
        name:
          type: string
          pattern: '^[a-z0-9-]+$'
        owner:
          type: string
          ui:field: OwnerPicker
  
  steps:
    - id: fetch
      name: Fetch template
      action: fetch:template
      input:
        url: ./skeleton
        values:
          name: ${{ parameters.name }}
          owner: ${{ parameters.owner }}
    
    - id: publish
      name: Publish to GitHub
      action: publish:github
      input:
        repoUrl: github.com?owner=myorg&repo=${{ parameters.name }}
        defaultBranch: main
    
    - 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 }}
```

→ "New service" 클릭 → 자동 repo + catalog 등록.

### Skeleton (template files)
```
template/
├── skeleton/
│   ├── catalog-info.yaml.hbs
│   ├── README.md.hbs
│   ├── package.json.hbs
│   ├── src/
│   │   └── index.ts.hbs
│   ├── Dockerfile.hbs
│   └── .github/
│       └── workflows/
│           └── ci.yml.hbs
└── template.yaml
```

```yaml
# skeleton/catalog-info.yaml.hbs
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: ${{ values.name }}
spec:
  owner: ${{ values.owner }}
  type: service
  lifecycle: experimental
```

### TechDocs (docs in repo)
```
service/
├── catalog-info.yaml
├── docs/
│   ├── index.md
│   ├── api.md
│   └── runbooks.md
└── mkdocs.yml
```

```yaml
# mkdocs.yml
site_name: Orders Service
nav:
  - Home: index.md
  - API: api.md
  - Runbooks: runbooks.md
```

```yaml
# catalog-info.yaml
metadata:
  annotations:
    backstage.io/techdocs-ref: dir:.
```

→ Backstage 가 docs build + render.

### Plugins
```bash
yarn backstage-cli new --select plugin
```

→ Backstage UI 안 새 page / widget 추가.

```ts
// 자체 plugin
import { createPlugin, createRoutableExtension } from '@backstage/core-plugin-api';

export const myPlugin = createPlugin({
  id: 'my-plugin',
  routes: { root: rootRouteRef },
});

export const MyPage = myPlugin.provide(
  createRoutableExtension({
    name: 'MyPage',
    component: () => import('./components/MyPage').then(m => m.MyPage),
    mountPoint: rootRouteRef,
  })
);
```

### Plugin marketplace
```
- GitHub Actions
- Argo CD
- Sentry
- PagerDuty
- Snyk
- AWS / GCP cost
- CodeScene
- 사내 — 자체
```

### Cost dashboard
```yaml
metadata:
  annotations:
    aws.amazon.com/account-id: '123456'
    aws.amazon.com/cost-tags: 'env=prod;team=payments'
```

→ AWS 가 backstage 안 billing 연결.

### CI/CD integration
```yaml
metadata:
  annotations:
    github.com/project-slug: myorg/orders-service
    argocd/app-name: orders-prod
    jenkins.io/job-full-name: orders-service/main
```

→ Service page 안 "Recent builds", "Latest deploy".

### Permission
```yaml
# permission policy
apiVersion: ...
- principal: group:payments-team
  permissions:
    - catalog:entity:read:owned
    - scaffolder:action:execute:nodejs-service
```

### Search
```
Backstage 가 모든 catalog + docs 자동 검색.
"How to deploy" → tutorial / runbook 표시.
```

### Score / lint
```yaml
# Tech health
metadata:
  annotations:
    score-card-ids: 'has-readme,has-runbook,has-test-coverage'
```

→ Score card 가 service quality 측정.

### Soundcheck (Spotify) — 강력 lint
```yaml
- check: has-readme
  description: Component has a README
  steps: [check_path: README.md]

- check: has-tests
  steps: [grep: 'test', in: package.json]
```

→ 모든 service 의 quality gate.

### Self-service workflow
```
1. Dev 가 "New microservice" 클릭
2. Form 채우기 (name, team)
3. Backstage 가:
   - GitHub repo 생성
   - CI/CD setup
   - Docker registry
   - K8s namespace
   - PagerDuty integration
   - DNS 등록
4. 5분 안 production-ready.
```

### Catalog providers
```ts
// 자동 import — GitHub repos
{
  catalog: {
    providers: {
      github: {
        organization: 'myorg',
        catalogPath: '/catalog-info.yaml',
        schedule: { frequency: { hours: 1 } },
      },
    },
  },
}
```

→ 매 시간 GitHub 모든 repo scan + catalog 업데이트.

### Alternatives / similar
```
- Cortex (commercial)
- OpsLevel
- Roadie (managed Backstage)
- Port
- Atlassian Compass

→ Backstage 가 OSS + 큰 ecosystem.
```

### Adoption phases
```
Phase 1: Catalog (자동 import all services)
Phase 2: TechDocs
Phase 3: Scaffolder (1-2 template)
Phase 4: 통합 plugins (CI, monitoring)
Phase 5: Score / health metrics
```

→ 단계별 도입.

### 가치
```
- 새 dev onboarding 빠름
- Service ownership 명확
- Cross-team 협업
- 표준화 (template)
- Compliance (모든 service score)
```

### Self-host vs Roadie / managed
```
Self-host Backstage:
+ Full control
+ Free
- Maintenance overhead

Managed:
+ 빠른 시작
+ 관리 X
- 비용
```

→ 큰 organization = self-host. 작은 = managed.

## 🤔 의사결정 기준
| 상황 | 추천 |
|---|---|
| 큰 organization (50+ services) | Backstage |
| 작은 (10 미만) | Wiki / spreadsheet 충분 |
| Compliance / 거버넌스 | Backstage + score cards |
| 빠른 시작 | Roadie (managed) |
| 자체 plugin 필요 | Self-host |

## ❌ 안티패턴
- **catalog-info.yaml 없는 service**: 자동 catalog 안 됨.
- **Stale catalog (수동만)**: 항상 outdated. Provider 자동.
- **모든 거 Backstage 만**: 하나 종속. Tools + Backstage.
- **Scaffolder 가 강 process**: dev 가 다른 path. 가벼운 + helpful.
- **TechDocs 없음**: docs 분산.
- **Onboarding 없음**: 도구 만 — 사용 X.

## 🤖 LLM 활용 힌트
- Catalog + scaffolder + TechDocs 3종.
- 자동 catalog from git.
- Health score 가 quality lever.
- Roadie 가 가장 빠른 시작.

## 🔗 관련 문서
- [[Productivity_Documentation]]
- [[DevOps_ArgoCD_GitOps]]
- [[Arch_Module_Boundaries]]
