<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Control Plane Lab</title>
    <description>The latest articles on DEV Community by Control Plane Lab (@controlplanelab).</description>
    <link>https://dev.to/controlplanelab</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4232808%2Fec658a6b-a031-4e86-b735-f9fd479a9447.jpg</url>
      <title>DEV Community: Control Plane Lab</title>
      <link>https://dev.to/controlplanelab</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/controlplanelab"/>
    <language>en</language>
    <item>
      <title>Long-Form SEO Article: EP01 - Backstage vs Port (Substack / Dev.to / Medium)</title>
      <dc:creator>Control Plane Lab</dc:creator>
      <pubDate>Sun, 08 Nov 2026 00:20:41 +0000</pubDate>
      <link>https://dev.to/controlplanelab/long-form-seo-article-ep01-backstage-vs-port-substack-devto-medium-542o</link>
      <guid>https://dev.to/controlplanelab/long-form-seo-article-ep01-backstage-vs-port-substack-devto-medium-542o</guid>
      <description>&lt;h1&gt;
  
  
  Long-Form SEO Article: EP01 - Backstage vs Port (Substack / Dev.to / Medium)
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Target Canonical URL:&lt;/strong&gt; Substack Newsletter (&lt;code&gt;controlplanelab.substack.com&lt;/code&gt;)&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Secondary Syndication:&lt;/strong&gt; Dev.to &amp;amp; Medium (with canonical link tag)&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Target Keywords:&lt;/strong&gt; Spotify Backstage review, Backstage vs Port, Internal Developer Portal architecture, Kubernetes IDP comparison&lt;/p&gt;


&lt;h1&gt;
  
  
  Why Platform Teams Are Ditching Spotify Backstage: An Architectural Teardown
&lt;/h1&gt;

&lt;p&gt;Every platform engineering team hits the exact same crossroads: your developers are overwhelmed by Kubernetes YAML, fragmented microservices, and hidden cloud costs. Leadership demands an Internal Developer Portal (IDP). &lt;/p&gt;

&lt;p&gt;And the immediate consensus is almost always: &lt;em&gt;“Let’s just install Spotify Backstage.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Six months later, reality sets in. &lt;/p&gt;

&lt;p&gt;Backstage is not a ready-to-run developer portal. It is an open-source TypeScript framework that requires you to build, host, and maintain an internal SaaS product from scratch. And that maintenance wall is why a massive wave of platform engineering teams are pivoting to declarative, schema-driven alternatives like Port.&lt;/p&gt;

&lt;p&gt;In this breakdown, we examine the architectural reality of Backstage versus Port: data model rigidity, action execution security, and total cost of ownership (TCO).&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/dimIWzLGu7k" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;br&gt;
&lt;em&gt;(Watch the full 10-minute video walkthrough above or check out our open-source blueprints repository on &lt;a href="https://github.com/ControlPlaneLab/blueprints/tree/main/EP01-backstage-vs-port" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  1. The Monolith Problem: When Frameworks Pretend to Be Products
&lt;/h2&gt;

&lt;p&gt;When you run &lt;code&gt;npx @backstage/create-app&lt;/code&gt;, you are not deploying a portal. You are scaffolding a full-stack monorepo with a React frontend and a Node.js Express backend.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-backstage-app/
├── packages/
│   ├── app/         &amp;lt;-- React Frontend
│   └── backend/     &amp;lt;-- Node.js Express Server
├── plugins/         &amp;lt;-- Bespoke TypeScript code
└── package.json     &amp;lt;-- 1,200+ npm dependencies
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every integration—whether PagerDuty, Datadog, Argo CD, or Kubernetes—is an npm plugin compiled directly into your application bundle.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Dependency Tax
&lt;/h3&gt;

&lt;p&gt;The Backstage core team releases updates constantly. Monthly version bumps frequently introduce breaking API changes across community plugins. Unless your platform team includes full-time TypeScript developers who enjoy debugging React dependency conflicts, your portal roadmap quickly grinds to a halt just keeping the lights on.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Data Modeling: Static Entities vs. Dynamic Graphs
&lt;/h2&gt;

&lt;p&gt;Backstage relies on a rigid entity model defined via &lt;code&gt;catalog-info.yaml&lt;/code&gt; files committed into source code repositories:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;backstage.io/v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Component&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;payment-service&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Handles Stripe webhooks&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;service&lt;/span&gt;
  &lt;span class="na"&gt;lifecycle&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production&lt;/span&gt;
  &lt;span class="na"&gt;owner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;platform-team&lt;/span&gt;
  &lt;span class="na"&gt;system&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;billing-system&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works well for static service catalogs. But modern cloud platforms are &lt;strong&gt;dynamic graph networks&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;If you want to model ephemeral preview environments, cloud RDS clusters, or AWS IAM roles with dynamic relationships, Backstage forces you to write custom TypeScript entity processors.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Port Alternative: Custom Graph Ontologies
&lt;/h3&gt;

&lt;p&gt;Port treats your entire engineering organization as an extensible graph database of blueprints. You don't write React or TypeScript. Instead, you declare blueprints via simple JSON or Terraform:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"identifier"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"microservice"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Microservice"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"schema"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"pci_compliant"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"boolean"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"monthly_spend"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"number"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"replicas"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"number"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"relations"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"hostedInCluster"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"kubernetes-cluster"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ephemeralPreviews"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"preview-env"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"many"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How does the data get into Port? Instead of polling Git for static YAML, Port uses lightweight open-source &lt;strong&gt;Ocean exporters&lt;/strong&gt; running inside your Kubernetes clusters or CI pipelines. The exporter watches cluster resources, maps them against your blueprint schema, and pushes state updates over HTTPS. No inbound ports opened, and no cluster secrets leave your network.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. The Execution Plane: Where Do Your Credentials Live?
&lt;/h2&gt;

&lt;p&gt;When developers use a Golden Path to spin up an environment, where do the credentials live?&lt;/p&gt;

&lt;h3&gt;
  
  
  Backstage Scaffolder
&lt;/h3&gt;

&lt;p&gt;In Backstage, the Scaffolder engine executes actions inside the Backstage backend container. This means your Backstage pod needs direct access to GitHub Organization PATs and cloud IAM roles. This creates a significant blast radius if the portal pod is compromised.&lt;/p&gt;

&lt;h3&gt;
  
  
  Port's Decoupled Architecture
&lt;/h3&gt;

&lt;p&gt;Port completely decouples the UI from the execution plane. When a developer clicks "Spin Up Ephemeral Preview" in Port:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Port does not execute the deployment.&lt;/li&gt;
&lt;li&gt;It emits an authenticated event to an internal GitHub Actions runner, Argo Workflow, or Kafka topic.&lt;/li&gt;
&lt;li&gt;The execution happens entirely within your private VPC runners.&lt;/li&gt;
&lt;li&gt;Progress logs stream back to the UI.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Zero credentials leave your secure infrastructure.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Total Cost of Ownership (TCO)
&lt;/h2&gt;

&lt;p&gt;Let’s look at the actual numbers:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Spotify Backstage&lt;/th&gt;
&lt;th&gt;Port (Declarative IDP)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Software License&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$0 (Open Source)&lt;/td&gt;
&lt;td&gt;SaaS fee&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Required Engineering&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1.5 – 2 Full-Time Engineers&lt;/td&gt;
&lt;td&gt;0.1 – 0.2 FTE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Annual Labor Upkeep&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~$300,000 – $450,000&lt;/td&gt;
&lt;td&gt;~$20,000 – $35,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Time to MVP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;4 – 6 Months&lt;/td&gt;
&lt;td&gt;1 – 2 Weeks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ongoing Maintenance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High (TypeScript upgrades)&lt;/td&gt;
&lt;td&gt;Low (Declarative schema)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Open source is only free if engineering time has zero value.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. The Decision Matrix
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Choose Spotify Backstage if:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You operate in a strictly air-gapped environment with zero SaaS tolerance.&lt;/li&gt;
&lt;li&gt;You require completely bespoke React UI workflows that no configurable SaaS can provide.&lt;/li&gt;
&lt;li&gt;You have dedicated frontend developers within your platform engineering organization.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Choose Port if:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You are a lean platform team (1–5 engineers) that needs immediate business value.&lt;/li&gt;
&lt;li&gt;Your infrastructure runs on Kubernetes, GitOps, and modern cloud primitives.&lt;/li&gt;
&lt;li&gt;You want zero framework maintenance overhead.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Clone the Blueprints &amp;amp; Manifests
&lt;/h2&gt;

&lt;p&gt;All architecture diagrams, Backstage catalog files, and Port JSON blueprints shown in this teardown are open source:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;Clone the Blueprints on GitHub:&lt;/strong&gt; &lt;a href="https://github.com/ControlPlaneLab/blueprints/tree/main/EP01-backstage-vs-port" rel="noopener noreferrer"&gt;ControlPlaneLab/blueprints&lt;/a&gt;&lt;br&gt;&lt;br&gt;
👉 &lt;strong&gt;Subscribe to Control Plane Lab on YouTube:&lt;/strong&gt; &lt;a href="https://www.youtube.com/@ControlPlaneLab" rel="noopener noreferrer"&gt;@ControlPlaneLab&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>kubernetes</category>
      <category>architecture</category>
      <category>cloud</category>
    </item>
  </channel>
</rss>
