The single source of truth for how the builder deploys your service. Every deploy reads this file. The builder generates Dockerfiles, provisions databases, configures OAuth, registers routes, and sets up DNS — all from this one file.
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: my-svc
description: My API service
annotations:
insureco.io/framework: express
spec:
type: service
lifecycle: production
owner: my-org # your org slug from Bio-ID JWT
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: my-svc
annotations:
insureco.io/framework: express # REQUIRED
insureco.io/catalog-version: "0.5.0" # required for spec.tests
insureco.io/pod-tier: nano # nano|small|medium|large|xlarge
insureco.io/health-endpoint: /health # REQUIRED (0.2.0+)
spec:
type: service
lifecycle: production
owner: my-org # REQUIRED (0.2.0+), not 'unknown'
routes:
- path: /api/my-svc/data
methods: [GET, POST]
auth: required
gas: 1
databases:
- type: mongodb # → MONGODB_URI
- type: redis # → REDIS_URL
internalDependencies:
- service: septor
- service: iec-wallet
port: 3000
tests:
smoke:
- path: /health
expect: 200
| Framework | Start Command | Port | Health |
|---|---|---|---|
express | node dist/index.js | 3000 | /health |
nextjs | npm start | 3000 | /api/health |
hono | node dist/index.js | 3000 | /health |
fastify | node dist/index.js | 3000 | /health |
worker | node dist/worker.js | 3000 | none |
static | nginx | 80 | /health |
Key rule: internalDependencies for everything without Bio-ID scopes. dependencies only when the target service defines explicit scope grants.
# Use internalDependencies for platform services and most internal services:
spec:
internalDependencies:
- service: septor # → SEPTOR_URL
- service: iec-wallet # → IEC_WALLET_URL
- service: iec-queue # → IEC_QUEUE_URL
# Use dependencies ONLY for services with Bio-ID scope grants:
spec:
dependencies:
- service: raterspot
transport: direct # injects RATERSPOT_URL
scopes: [raterspot:rate] # REQUIRED, cannot be empty
Common mistake: using dependencies with scopes: [] — that is a validation error.
Common mistake: using transport: gateway expecting a URL env var — gateway does not inject a URL.
routes:
- path: /api/my-svc/screen
methods: [POST]
auth: required # required | none | service | public
gas: 5 # tokens per successful call. 0=free. omit=default(1)
Auth values:
required — valid user JWT required (Janus verifies)service — service-to-service JWT requiredpublic — no auth, but still meterednone — no auth, not metered (use for health checks)spec:
queues:
- name: process-claim
endpoint: /internal/jobs/process-claim
concurrency: 5
retries: 3
retryDelayMs: 5000
timeoutMs: 30000
schedules:
- name: nightly-sync
cron: "0 2 * * *"
endpoint: /internal/cron/nightly-sync
timezone: America/Denver
timeoutMs: 60000
Queue and schedule endpoints must NOT be listed under routes:. They are internal-only.
annotations:
insureco.io/framework: express # REQUIRED
insureco.io/catalog-version: "0.5.0" # required for spec.tests
insureco.io/node-version: "20" # default: 20
insureco.io/pod-tier: nano # default: nano
insureco.io/health-endpoint: /health # REQUIRED (0.2.0+)
insureco.io/port: "3000" # default: 3000
insureco.io/build-command: npm run build
insureco.io/start-command: node dist/index.js
insureco.io/output-dir: dist
insureco.io/openapi: openapi.yaml # enables auto-generated UI tile
If your service lives in a subdirectory (e.g., web/ inside a monorepo), add .iec.yaml at the repo root:
# .iec.yaml (repo root)
appPath: web
The builder then reads catalog-info.yaml from web/catalog-info.yaml, uses web/ as the build context, and resolves Dockerfile and Helm chart paths relative to web/. No other changes needed.
See the .iec.yaml — Builder Config reference for all supported fields.
metadata.name becomes your hostname: my-svc.tawa.insureco.ionano — start here, upgrade when you have data.iec.yaml at the repo root with appPath: <subdir> — see .iec.yaml referenceLast updated: February 28, 2026