<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Catalog on zharif.my</title>
        <link>https://zharif.my/tags/catalog/</link>
        <description>Recent content in Catalog on zharif.my</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>en-us</language>
        <lastBuildDate>Sat, 18 Apr 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://zharif.my/tags/catalog/index.xml" rel="self" type="application/rss+xml" /><item>
        <title>Self-Service Infrastructure with Backstage</title>
        <link>https://zharif.my/posts/backstage-homelab/</link>
        <pubDate>Sat, 18 Apr 2026 00:00:00 +0000</pubDate>
        
        <guid>https://zharif.my/posts/backstage-homelab/</guid>
        <description>&lt;img src="https://images.unsplash.com/photo-1551288049-bebda4e38f71?w=800&amp;h=400&amp;fit=crop" alt="Featured image of post Self-Service Infrastructure with Backstage" /&gt;&lt;h2 id=&#34;why-self-service-matters&#34;&gt;Why Self-Service Matters
&lt;/h2&gt;&lt;p&gt;In my homelab, I was the bottleneck. Every new Kubernetes cluster meant:&lt;/p&gt;
&lt;pre class=&#34;mermaid&#34;&gt;
  flowchart LR
    A[Create YAML] --&amp;gt; B[Find free IPs]
    B --&amp;gt; C[Configure node sizes]
    C --&amp;gt; D[Manually create Backstage catalog entries]
    D --&amp;gt; E[Open PR]
    E --&amp;gt; F[Wait for review]
&lt;/pre&gt;

&lt;p&gt;That&amp;rsquo;s 6 steps where 4 could be automated.&lt;/p&gt;
&lt;p&gt;The insight: infrastructure already defined as YAML. Backstage should consume that same YAML and generate its own catalog entries.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Real-world constraint&lt;/strong&gt;: I deploy clusters infrequently (quarterly?), so I forget the steps. The templated approach ensures consistency whether I do this once a month or once a year.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This post covers the Backstage integration for homelab infrastructure. See the architecture overview for how it fits in the broader platform.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;two-integration-points&#34;&gt;Two Integration Points
&lt;/h2&gt;&lt;p&gt;Backstage integrates with the homelab infrastructure in two ways:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Resource Catalog&lt;/strong&gt; — auto-generated entities from infrastructure YAML configurations&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Software Templates&lt;/strong&gt; — scaffolder templates for self-service provisioning&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&#34;mermaid&#34;&gt;
  graph LR
    subgraph &amp;#34;Configuration&amp;#34;
        C[configurations/*.yaml]
    end
    
    subgraph &amp;#34;Generation&amp;#34;
        G[generate_backstage_catalog.py]
    end
    
    subgraph &amp;#34;Backstage&amp;#34;
        R[Resource Catalog]
        T[Software Templates]
    end
    
    C --&amp;gt; G
    G --&amp;gt; R
    G --&amp;gt; T
&lt;/pre&gt;

&lt;h2 id=&#34;auto-generated-catalog&#34;&gt;Auto-Generated Catalog
&lt;/h2&gt;&lt;p&gt;Running &lt;code&gt;make backstage-catalog&lt;/code&gt; generates Backstage Resource entities from configurations:&lt;/p&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;make backstage-catalog
# ✓ kubernetes--prod-k8s.yaml (prod-k8s, disabled)
# ✓ kubernetes--dev-k8s.yaml (dev-k8s, disabled)
# ✓ docker--prod-docker-lxc.yaml (prod-docker-lxc, disabled)
# ✓ docker--dev-docker-lxc.yaml (dev-docker-lxc, enabled)&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;generated-entity-example&#34;&gt;Generated Entity Example
&lt;/h3&gt;&lt;p&gt;Each generated YAML file is a Backstage Resource:&lt;/p&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34;&gt;# backstage/catalog/kubernetes--prod-k8s.yaml
apiVersion: backstage.io/v1alpha1
kind: Resource
metadata:
  name: prod-k8s
  description: Talos kubernetes cluster configuration for production environment
  annotations:
    github.com/project-slug: your-org/your-infra-repo
    homelab.dev/configuration-file: configurations/kubernetes/prod-k8s.yaml
    homelab.dev/resource-type: kubernetes
    homelab.dev/schema-file: configuration_schemas/kubernetes.schema.yaml
    backstage.io/techdocs-entity: component:terraform-module-kubernetes
    homelab.dev/cluster-name: prod-k8s
    homelab.dev/talos-version: v1.12.4
    homelab.dev/kubernetes-version: v1.35.0
    homelab.dev/vip-address: 192.168.62.20
  labels:
    homelab.dev/enabled: &amp;#39;false&amp;#39;
    homelab.dev/environment: prod
    homelab.dev/control-plane-count: &amp;#39;3&amp;#39;
    homelab.dev/worker-count: &amp;#39;3&amp;#39;
    homelab.dev/size-control_plane-cpu: &amp;#39;4&amp;#39;
    homelab.dev/size-control_plane-memory: &amp;#39;8192&amp;#39;
    homelab.dev/size-worker-cpu: &amp;#39;10&amp;#39;
    homelab.dev/size-worker-memory: &amp;#39;49152&amp;#39;
  tags:
    - disabled
    - kubernetes
    - prod
    - proxmox
    - talos
spec:
  type: kubernetes-cluster
  lifecycle: experimental
  owner: group:default/homelab-admins
  system: tf-infra-homelab
  dependsOn:
    - component:default/terraform-module-kubernetes&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;docker-cluster-entity&#34;&gt;Docker Cluster Entity
&lt;/h3&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34;&gt;# backstage/catalog/docker--prod-docker-lxc.yaml
apiVersion: backstage.io/v1alpha1
kind: Resource
metadata:
  name: prod-docker-lxc
  description: Docker configuration on lxc for production environment
  annotations:
    github.com/project-slug: your-org/your-infra-repo
    homelab.dev/configuration-file: configurations/docker/prod-docker-lxc.yaml
    homelab.dev/resource-type: docker
    homelab.dev/cluster-name: prod-docker-lxc
    homelab.dev/cluster-type: lxc
    homelab.dev/vip-address: 192.168.61.20
  labels:
    homelab.dev/enabled: &amp;#39;false&amp;#39;
    homelab.dev/environment: prod
    homelab.dev/worker-count: &amp;#39;3&amp;#39;
    homelab.dev/size-medium-cpu: &amp;#39;8&amp;#39;
    homelab.dev/size-medium-memory: &amp;#39;32768&amp;#39;
  tags:
    - disabled
    - docker
    - lxc
    - prod
    - proxmox
spec:
  type: docker-cluster
  lifecycle: experimental
  owner: group:default/homelab-admins
  system: tf-infra-homelab
  dependsOn:
    - component:default/terraform-module-docker&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;metadata-extraction&#34;&gt;Metadata Extraction
&lt;/h3&gt;&lt;p&gt;The generation script extracts key metadata from configurations:&lt;/p&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34;&gt;# scripts/generate_backstage_catalog.py

def extract_kubernetes_metadata(config: dict) -&amp;gt; dict:
    &amp;#34;&amp;#34;&amp;#34;Extract catalog-relevant metadata from a Kubernetes configuration.&amp;#34;&amp;#34;&amp;#34;
    annotations = {}
    labels = {}
    
    cluster = config.get(&amp;#34;cluster&amp;#34;, {})
    annotations[&amp;#34;homelab.dev/cluster-name&amp;#34;] = cluster.get(&amp;#34;name&amp;#34;, &amp;#34;&amp;#34;)
    
    talos = cluster.get(&amp;#34;talos&amp;#34;, {}) or {}
    annotations[&amp;#34;homelab.dev/talos-version&amp;#34;] = talos.get(&amp;#34;version&amp;#34;, &amp;#34;&amp;#34;)
    annotations[&amp;#34;homelab.dev/kubernetes-version&amp;#34;] = cluster.get(&amp;#34;kubernetes_version&amp;#34;, &amp;#34;&amp;#34;)
    
    cp_nodes = config.get(&amp;#34;control_plane_nodes&amp;#34;, {}).get(&amp;#34;nodes&amp;#34;, [])
    worker_nodes = config.get(&amp;#34;worker_nodes&amp;#34;, {}).get(&amp;#34;nodes&amp;#34;, [])
    labels[&amp;#34;homelab.dev/control-plane-count&amp;#34;] = str(len(cp_nodes))
    labels[&amp;#34;homelab.dev/worker-count&amp;#34;] = str(len(worker_nodes))
    
    cp_vip = config.get(&amp;#34;control_plane_nodes&amp;#34;, {}).get(&amp;#34;vip&amp;#34;, {})
    if cp_vip and cp_vip.get(&amp;#34;enabled&amp;#34;):
        annotations[&amp;#34;homelab.dev/vip-address&amp;#34;] = cp_vip.get(&amp;#34;address&amp;#34;, &amp;#34;&amp;#34;)
    
    sizes = config.get(&amp;#34;node_size_configuration&amp;#34;, {})
    for size_name, size_spec in sizes.items():
        labels[f&amp;#34;homelab.dev/size-{size_name}-cpu&amp;#34;] = str(size_spec.get(&amp;#34;cpu&amp;#34;, &amp;#34;&amp;#34;))
        labels[f&amp;#34;homelab.dev/size-{size_name}-memory&amp;#34;] = str(size_spec.get(&amp;#34;memory&amp;#34;, &amp;#34;&amp;#34;))
    
    return {&amp;#34;annotations&amp;#34;: annotations, &amp;#34;labels&amp;#34;: labels}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This enables filtering in Backstage:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;homelab.dev/environment=prod&lt;/code&gt; — production clusters&lt;/li&gt;
&lt;li&gt;&lt;code&gt;homelab.dev/enabled=true&lt;/code&gt; — currently deployed&lt;/li&gt;
&lt;li&gt;&lt;code&gt;homelab.dev/size-worker-memory=49152&lt;/code&gt; — large workers&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;catalog-info-definition&#34;&gt;Catalog-Info Definition
&lt;/h2&gt;&lt;p&gt;The root &lt;code&gt;catalog-info.yaml&lt;/code&gt; defines the domain, system, and components:&lt;/p&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34;&gt;# Domain: homelab
apiVersion: backstage.io/v1alpha1
kind: Domain
metadata:
  name: homelab
  description: Self-hosted homelab infrastructure managed with Terraform and Proxmox
  annotations:
    backstage.io/techdocs-ref: dir:.
    github.com/project-slug: your-org/your-infra-repo
spec:
  owner: group:default/homelab-admins

---
# System: tf-infra-homelab
apiVersion: backstage.io/v1alpha1
kind: System
metadata:
  name: tf-infra-homelab
  description: Terraform-managed homelab infrastructure provisioning system
spec:
  owner: group:default/homelab-admins
  domain: homelab

---
# Component: terraform-module-kubernetes
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: terraform-module-kubernetes
  description: Terraform module for provisioning Kubernetes (Talos) clusters on Proxmox
spec:
  type: terraform-module
  lifecycle: production
  owner: group:default/homelab-admins
  system: tf-infra-homelab

---
# Location: discovers auto-generated resources
apiVersion: backstage.io/v1alpha1
kind: Location
metadata:
  name: tf-infra-homelab-resources
spec:
  targets:
    - ./backstage/catalog/*.yaml&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;software-templates&#34;&gt;Software Templates
&lt;/h2&gt;&lt;p&gt;The Backstage scaffolder templates enable self-service provisioning:&lt;/p&gt;
&lt;h3 id=&#34;kubernetes-template&#34;&gt;Kubernetes Template
&lt;/h3&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34;&gt;# backstage/templates/kubernetes/template.yaml
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
  name: provision-kubernetes-cluster
  title: Provision Kubernetes Cluster
  description: Create a new Talos-based Kubernetes cluster configuration on Proxmox
  tags:
    - terraform
    - kubernetes
    - talos
    - proxmox
    - homelab
spec:
  owner: group:default/homelab-admins
  type: infrastructure
  system: tf-infra-homelab&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;template-parameters&#34;&gt;Template Parameters
&lt;/h3&gt;&lt;p&gt;The template accepts parameters for cluster configuration:&lt;/p&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34;&gt;parameters:
  - title: Cluster Identity
    required:
      - name
      - environment
    properties:
      name:
        title: Cluster Name
        type: string
        pattern: &amp;#34;^[a-z][a-z0-9-]&amp;#43;$&amp;#34;
      environment:
        title: Environment
        type: string
        enum:
          - dev
          - staging
          - prod

  - title: Cluster Configuration
    properties:
      talos_version:
        title: Talos Version
        type: string
        default: v1.12.4
      kubernetes_version:
        title: Kubernetes Version
        type: string
        default: v1.35.0
      disable_default_cni:
        title: Disable Default CNI
        type: boolean
        default: true

  - title: Control Plane Nodes
    properties:
      cp_count:
        title: Number of Control Plane Nodes
        type: integer
        minimum: 1
        maximum: 5
        default: 3
      cp_cpu:
        title: CPU Cores per CP Node
        type: integer
        default: 4
      cp_memory:
        title: Memory per CP Node (MB)
        type: integer
        default: 8192

  - title: Worker Nodes
    properties:
      worker_count:
        title: Number of Workers
        type: integer
        default: 3
      worker_cpu:
        title: CPU Cores per Worker
        type: integer
        default: 8
      worker_memory:
        title: Memory per Worker (MB)
        type: integer
        default: 16384&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;template-steps&#34;&gt;Template Steps
&lt;/h3&gt;&lt;p&gt;Each template has three steps:&lt;/p&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34;&gt;steps:
  - id: generate
    name: Generate Configuration
    action: fetch:template
    input:
      url: ./skeleton/kubernetes
      targetPath: configurations/kubernetes
      values:
        name: ${{ parameters.name }}
        talos_version: ${{ parameters.talos_version }}
        cp_count: ${{ parameters.cp_count }}

  - id: generate-catalog
    name: Generate Backstage Catalog Entry
    action: fetch:template
    input:
      url: ./skeleton/backstage/catalog
      targetPath: backstage/catalog

  - id: publish
    name: Open Pull Request
    action: publish:github:pull-request
    input:
      repoUrl: github.com?repo=tf-infra-homelab&amp;amp;owner=your-org
      title: &amp;#34;feat: provision Kubernetes cluster ${{ parameters.name }}&amp;#34;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;user-flow&#34;&gt;User Flow
&lt;/h3&gt;&lt;p&gt;In Backstage, users:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Choose template&lt;/strong&gt; — &amp;ldquo;Provision Kubernetes Cluster&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fill parameters&lt;/strong&gt; — name, environment, node sizes&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Submit&lt;/strong&gt; — opens a PR automatically&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Review&lt;/strong&gt; — maintainers approve the PR&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Apply&lt;/strong&gt; — Terraform provisions the cluster&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&#34;mermaid&#34;&gt;
  sequenceDiagram
    participant User
    participant BS as Backstage
    participant GH as GitHub
    participant TF as Terraform
    participant PVE as Proxmox
    participant Talos
    participant Flux
    
    User-&amp;gt;&amp;gt;BS: Create new cluster (fill form)
    BS-&amp;gt;&amp;gt;GH: Open PR with config files
    Maintainer-&amp;gt;&amp;gt;GH: Review and approve PR
    GH-&amp;gt;&amp;gt;TF: Merge triggers apply
    TF-&amp;gt;&amp;gt;PVE: Provision VMs
    PVE-&amp;gt;&amp;gt;Talos: Bootstrap cluster
    Talos-&amp;gt;&amp;gt;Flux: Install GitOps
&lt;/pre&gt;

&lt;h2 id=&#34;generation-script&#34;&gt;Generation Script
&lt;/h2&gt;&lt;p&gt;The full script in &lt;code&gt;scripts/generate_backstage_catalog.py&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34;&gt;#!/usr/bin/env python3
&amp;#34;&amp;#34;&amp;#34;Generate Backstage catalog Resource entities from configuration YAML files.&amp;#34;&amp;#34;&amp;#34;

import yaml
from pathlib import Path

REPO_ROOT = Path(__file__).resolve().parent.parent

def load_yaml(path: Path) -&amp;gt; dict:
    return yaml.safe_load(path.read_text()) or {}

def build_resource_entity(resource_type, environment_name, config):
    &amp;#34;&amp;#34;&amp;#34;Build a Backstage Resource entity from configuration.&amp;#34;&amp;#34;&amp;#34;
    entity = {
        &amp;#34;apiVersion&amp;#34;: &amp;#34;backstage.io/v1alpha1&amp;#34;,
        &amp;#34;kind&amp;#34;: &amp;#34;Resource&amp;#34;,
        &amp;#34;metadata&amp;#34;: {
            &amp;#34;name&amp;#34;: config.get(&amp;#34;name&amp;#34;, environment_name),
            &amp;#34;description&amp;#34;: config.get(&amp;#34;description&amp;#34;, &amp;#34;&amp;#34;),
            &amp;#34;annotations&amp;#34;: {...},
            &amp;#34;labels&amp;#34;: {...},
        },
        &amp;#34;spec&amp;#34;: {
            &amp;#34;type&amp;#34;: RESOURCE_TYPE_META[resource_type][&amp;#34;backstage_type&amp;#34;],
            &amp;#34;lifecycle&amp;#34;: &amp;#34;experimental&amp;#34;,
            &amp;#34;owner&amp;#34;: &amp;#34;group:default/homelab-admins&amp;#34;,
            &amp;#34;system&amp;#34;: &amp;#34;tf-infra-homelab&amp;#34;,
        },
    }
    return entity

def main():
    for config_file in (REPO_ROOT / &amp;#34;configurations&amp;#34;).rglob(&amp;#34;*.yaml&amp;#34;):
        config = load_yaml(config_file)
        entity = build_resource_entity(...)
        output_file.write_text(yaml.dump(entity))&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Run via Makefile:&lt;/p&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-makefile&#34;&gt;backstage-catalog:
  python3 scripts/generate_backstage_catalog.py&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;filtering-in-backstage&#34;&gt;Filtering in Backstage
&lt;/h2&gt;&lt;p&gt;With the generated metadata, users can filter in Backstage:&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;Filter&lt;/th&gt;
          &lt;th&gt;Use Case&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;homelab.dev/environment=prod&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;Production clusters&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;homelab.dev/enabled=true&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;Currently deployed&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;homelab.dev/control-plane-count=3&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;Full quorum&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;homelab.dev/size-worker-memory&amp;gt;=16384&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;Large workers&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;homelab.dev/talos-version=v1.12.*&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;Specific Talos version&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;what-most-people-get-wrong&#34;&gt;What Most People Get Wrong
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&amp;ldquo;Backstage is only for Kubernetes&amp;rdquo;&lt;/strong&gt; — It catalogs anything. My LXC, VMs, Docker clusters all have Backstage entries.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&amp;ldquo;Templates replace code review&amp;rdquo;&lt;/strong&gt; — My templates generate PRs. Human review still happens. Self-service ≠ unattended.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&amp;ldquo;Catalog must be perfect at launch&amp;rdquo;&lt;/strong&gt; — Start simple. The YAML-to-catalog pipeline can always regenerate.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;when-to-use--when-not-to-use&#34;&gt;When to Use / When NOT to Use
&lt;/h2&gt;&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;Use Backstage&lt;/th&gt;
          &lt;th&gt;Use direct Terraform&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;Team self-service&lt;/td&gt;
          &lt;td&gt;Single admin&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;10+ resources&lt;/td&gt;
          &lt;td&gt;&amp;lt;5 resources&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Need catalog UI&lt;/td&gt;
          &lt;td&gt;CLI is enough&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;whats-next&#34;&gt;What&amp;rsquo;s Next
&lt;/h2&gt;&lt;p&gt;Current areas of exploration:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;More templates&lt;/strong&gt; — VM and LXC provisioning templates&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Approval workflows&lt;/strong&gt; — notification to maintainers&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Status tracking&lt;/strong&gt; — integration with Terraform Cloud state&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The Backstage integration makes infrastructure self-serviceable while maintaining code review.&lt;/p&gt;
</description>
        </item>
        
    </channel>
</rss>
