Port: The Internal Developer Portal That Works Out of the Box
Port is an internal developer portal (IDP) built as a product rather than a framework. Where Backstage hands you a pile of React components and says “good luck,” Port hands you a working portal on day one. You model your engineering world through a flexible data model, connect your tools, and expose self-service actions — all through a UI or API, without writing and deploying custom code.
This guide covers everything a platform engineer needs to evaluate, deploy, and extend Port: the data model, blueprint system, integrations, self-service actions, scorecards, the Ocean framework, and how it honestly stacks up against Backstage.
What Port Is and Why It Exists
An internal developer portal solves the “where does this live?” problem. Services scatter across GitHub repos, infrastructure lives in Terraform state, incidents land in PagerDuty, teams are defined in Okta, and nobody has a single view of what depends on what, who owns it, or whether it’s production-ready. An IDP creates that view — and builds self-service tooling on top of it so developers can act on what they see without creating a ticket and waiting three days.
Port’s core bet is that most organizations don’t want to build and maintain a custom portal. They want to define their data model and connect their tools, not maintain a Node.js/React monolith with a custom plugin for every integration. Port is cloud-hosted SaaS: Port runs the infrastructure, you define the model.
The platform has seven core capabilities:
- Software Catalog — a unified SDLC view of all your entities (services, clusters, environments, pipelines, etc.)
- Blueprints — the data model that defines what entities exist and what properties they have
- Self-Service Actions — developer-triggered operations backed by GitHub Actions, webhooks, GitLab pipelines, or Terraform
- Scorecards — production readiness checks and maturity tracking against defined standards
- Workflow Automation — event-driven automations triggered by catalog changes
- AI Agents — custom agents with defined goals and access to catalog context
- Interface Designer — custom dashboards per team or persona
Architecture: How Port Works
Port is fully cloud-hosted. There’s no backend to deploy, no database to manage. You sign up at app.getport.io, define your data model in the Builder, and connect integrations that push data in.
┌─────────────────────────────────────────────────────────────┐
│ Port SaaS Platform │
│ │
│ ┌──────────────┐ ┌─────────────┐ ┌────────────────────┐ │
│ │ Blueprint │ │ Software │ │ Self-Service │ │
│ │ Builder │ │ Catalog │ │ Actions │ │
│ └──────────────┘ └─────────────┘ └────────────────────┘ │
│ │
│ ┌──────────────┐ ┌─────────────┐ ┌────────────────────┐ │
│ │ Scorecards │ │ Workflow │ │ Dashboards / │ │
│ │ │ │ Automation │ │ Interface Designer│ │
│ └──────────────┘ └─────────────┘ └────────────────────┘ │
│ │
│ REST API / Terraform Provider / Pulumi Provider │
└─────────────────────────────────────────────────────────────┘
▲ ▲ ▲
│ │ │
Ocean Integrations GitHub App Webhooks /
(K8s, GitLab, AWS, (repo data, Custom ingestion
PagerDuty, etc.) PR events)
Data flow: Integrations pull data from external systems (or receive webhooks) and push entity data to Port’s API. Port stores entities in its own managed database keyed by blueprint + identifier. The catalog UI reads from this database. Self-service actions send invocation payloads outward to your CI/CD systems — Port never holds your secrets or executes code directly.
API-first: Everything Port does through its UI is available via REST API. Port also provides a Terraform provider and Pulumi provider for managing the data model as code.
The Blueprint System
Blueprints are Port’s data model. A blueprint defines a type of entity — like Service, Kubernetes Cluster, Deployment, PagerDuty Service, or Cloud Account. Every entity in Port is an instance of a blueprint.
Blueprint Structure
A blueprint is a JSON document with this shape:
|
|
Property Types
Port supports a rich set of property types:
| Type | Format | Use Case |
|---|---|---|
string |
— | Free text |
string |
url |
Clickable links |
string |
email |
Email addresses |
string |
user |
Reference to a Port user |
string |
team |
Reference to a Port team |
string |
date-time |
Timestamps |
string |
timer |
TTL / expiry countdown |
string |
markdown |
Rendered Markdown content |
string |
yaml |
YAML object viewer |
number |
— | Integers and floats |
boolean |
— | True/false flags |
array |
— | Lists of values |
object |
— | Arbitrary JSON objects |
string |
enum |
Dropdown with defined values |
One important constraint: the type of a property is immutable after creation. Plan your schema carefully — changing a property type requires deleting and recreating it, which loses existing data for that field.
Calculation Properties
Calculation properties derive their value from a formula applied to other properties. They’re defined as jq expressions:
|
|
Mirror Properties
Mirror properties pull values from related entities into the current blueprint. For example, if a Deployment is related to a Service, you can mirror Service.tier onto Deployment so it’s visible without navigating the relation:
|
|
Relations Between Blueprints
Relations connect blueprints to build a graph of your engineering world. There are two relation types:
- Single (
many: false) — one-to-one: a deployment relates to exactly one service - Many (
many: true) — one-to-many: a service depends on multiple packages
|
|
One constraint: a relation cannot have both required: true and many: true simultaneously.
Relations are how Port builds context. A Kubernetes Pod blueprint relates to a Kubernetes Node, which relates to a Kubernetes Cluster, which relates to a Cloud Account. Navigate the graph in the UI and you get instant context: which pods are running, on which nodes, in which cluster, owned by which team, with which on-call engineer.
Software Catalog: Populating Entities
The catalog is only useful if it stays current. Port provides three main paths to populate it.
1. Ocean Integrations (Pull-Based)
Ocean integrations are the primary mechanism. Each integration is a standalone process that connects to an external system, transforms data with JQ mappings, and pushes entities to Port’s API. Most integrations can be hosted by Port (zero infrastructure overhead) or self-hosted in your own cluster.
Install the Kubernetes integration via Helm:
|
|
After install, the exporter runs a continuous sync loop: on startup it does a full resync, then watches the Kubernetes API for create/update/delete events and pushes changes to Port in real-time.
The integration maps Kubernetes objects to blueprints through a YAML configuration that lives in Port’s UI (editable post-install):
|
|
The selector.query is a JQ expression that filters which resources to ingest. The mappings section maps Kubernetes object fields to blueprint properties using JQ.
2. GitHub Integration
The GitHub integration installs as a GitHub App and syncs repositories, pull requests, workflow runs, issues, deployments, branches, tags, Dependabot alerts, code scanning alerts, and file contents.
Per-repo or org-wide configuration lives in port-app-config.yml:
|
|
The file kind is powerful: it lets you ingest structured data from YAML/JSON files in your repos — service manifests, dependency lists, SLO definitions — and create entities from them.
Note: The legacy GitHub integration sunsets July 15, 2026. Migrate to the Ocean-powered GitHub integration before then.
3. Port’s REST API
For custom data sources — internal databases, legacy CMDBs, homegrown tooling — push entities directly via the API:
|
|
Get a token first:
|
|
The API supports upsert semantics: POST with upsert=true creates or updates based on identifier, making it idempotent for CI/CD pipelines.
Self-Service Actions
Self-service actions let developers trigger predefined operations from the Port UI (or API) without needing access to the underlying infrastructure. The key design principle: Port sends a payload to your backend — it doesn’t execute code itself. Your existing CI/CD systems remain the execution engine.
Action Anatomy
Every action has three parts:
- Frontend inputs — the form developers fill out (validated before submission)
- Backend invocation — how Port triggers the execution
- Audit trail — Port records every execution, its inputs, status, and output link
Defining an Action (JSON)
|
|
GitHub Actions Backend
The GitHub workflow receives Port’s payload via workflow_dispatch inputs:
|
|
Backend Types Compared
| Backend | Type Value | Best For |
|---|---|---|
| GitHub Actions | GITHUB |
Most CI/CD workflows |
| GitLab Pipelines | GITLAB |
GitLab-native orgs |
| Azure DevOps | AZURE_DEVOPS |
Microsoft-heavy shops |
| Webhook | WEBHOOK |
Jenkins, custom APIs, n8n |
| Kafka | KAFKA |
Event-driven, async operations |
| Upsert Entity | UPSERT_ENTITY |
Simple catalog updates, no external backend needed |
Webhook Backend Example
For operations backed by Jenkins, n8n, or any HTTP endpoint:
|
|
Action Approvals
For sensitive operations — delete a service, modify production — Port supports multi-step approval flows. Define approvers by team or role; the action queues until approved, then executes. All approvals and rejections are captured in the audit log.
Scorecards
Scorecards answer the question: “Are our services production-ready, and how do we measure progress?” They evaluate entities against defined rules and surface compliance status across your entire catalog.
How Scorecards Work
A scorecard attaches to a blueprint and defines:
- Levels — hierarchical stages (default: Basic, Bronze, Silver, Gold)
- Rules — conditions that must pass to reach a given level
- Filters — optional JQ expressions to scope which entities are evaluated
An entity starts at Basic and advances only when it passes all rules for a level. This creates a clear progression path and prevents cherry-picking easy wins.
Production Readiness Scorecard (Full Example)
|
|
Scorecard Operators
| Operator | Meaning |
|---|---|
= |
Equals |
!= |
Not equals |
< |
Less than |
> |
Greater than |
<= |
Less than or equal |
>= |
Greater than or equal |
contains |
String contains |
doesNotContain |
String does not contain |
isEmpty |
Property is null or empty |
isNotEmpty |
Property has a value |
in |
Value is in array |
notIn |
Value is not in array |
DORA Metrics Scorecard
Port provides a built-in DORA metrics integration. Once you’ve connected GitHub (for deployment data) and PagerDuty (for incident data), you can configure a DORA scorecard benchmarked against the official DORA report thresholds:
|
|
Scorecard Automation
Scorecards can trigger automations. When a service degrades below a threshold (e.g., drops from Silver to Bronze), Port can automatically open a Jira ticket, send a Slack notification to the owning team, or trigger a GitHub Action to run a health check. This closes the loop from “we see the problem” to “someone is fixing it.”
Key Integrations
Port’s integration catalog covers over 50 systems. Here’s what the major ones provide.
GitHub
- Repositories, branches, pull requests, issues, releases, tags
- Workflow runs and workflow definitions
- Code scanning alerts and Dependabot alerts
- Deployments and environments
- File ingestion (parse YAML/JSON from repos into entities)
- Real-time sync via GitHub App webhooks
Configuration is per-repo (port-app-config.yml in .github/) or org-wide (.github-private/).
GitLab
- Projects, groups, merge requests, pipelines
- File kind support (similar to GitHub file ingestion)
- GitLab V2 integration simplifies token setup and improves performance
- Periodic sync and real-time webhook mode
Kubernetes
- Namespaces, nodes, pods, deployments, DaemonSets, StatefulSets, ReplicaSets, services
- CRDs from ArgoCD, Istio, Knative, OpenShift
- Deployed as Helm chart in-cluster, watches the Kubernetes API
- Automatic health status (compares desired vs available replicas)
- Container security analysis (privileged mode, resource limits)
Terraform
- Track Terraform Cloud/Enterprise workspaces and runs
- Import infrastructure resources (EC2, RDS, S3, etc.) into the catalog
- Terraform provider for managing Port’s own data model as code:
|
|
PagerDuty
- Services, incidents, schedules, on-call assignments, escalation policies, users
- Service analytics: MTTR, mean time to first acknowledge
- Incident analytics per service
- Real-time sync via webhooks for incident state changes
- Critical for on-call visibility: surface
oncall_userfrom PagerDuty as a property onServiceblueprints
AWS, GCP, Azure
Port has cloud integrations that ingest:
- AWS: EC2 instances, S3 buckets, ECR repositories, CloudFormation stacks, EKS clusters
- GCP: Projects, compute instances, GCS buckets
- Azure: Subscriptions, resource groups, AKS clusters, Azure DevOps work items
AWS integration supports a “hosted-by-Port” mode (OAuth, minimal setup) and self-hosted mode.
ArgoCD
- Applications, projects, deployment status
- Maps ArgoCD
Applicationresources to Port entities - Correlate ArgoCD deployment status with your service catalog entries
Security Integrations
- Snyk: Vulnerabilities, projects, license compliance
- SonarQube/SonarCloud: Code quality metrics, coverage, technical debt
- Wiz: Cloud security findings, misconfigurations
- Trivy: Container vulnerability scanning results
The Ocean Integration Framework
Ocean is the open-source Python framework that powers all of Port’s integrations. It’s available on GitHub (port-labs/ocean) under Apache 2.0. If Port doesn’t have a native integration for your tool, Ocean is how you build one.
Architecture
Ocean has two layers:
- Ocean Core (
port_ocean/) — the shared infrastructure: Port API authentication, entity ingestion pipelines, JQ transformation engine, event listeners (polling, webhooks, Kafka), resync orchestration - Integrations (
integrations/) — individual implementations for each third-party system
An integration developer writes only the third-party system logic: how to authenticate, how to fetch resources, how to handle webhooks. Ocean Core handles everything else.
How an Integration Works
Every Ocean integration follows a lifecycle:
- Startup: Load configuration, authenticate with the external system
- Initial resync: Fetch all current resources, map them with JQ, push to Port
- Event listening: Subscribe to webhooks or poll for changes
- Real-time updates: Push delta changes to Port as events arrive
- Periodic resync: Re-run full sync on a configurable schedule
Building a Custom Integration
An Ocean integration has this structure:
my-integration/
├── main.py # Core logic
├── integration.py # Configuration schema
├── pyproject.toml # Dependencies
└── .port/
├── spec.yaml # Integration metadata
└── resources/
├── blueprints.json # Default blueprints to create
└── port-app-config.yaml # Default entity mappings
A minimal main.py for a custom system:
|
|
The port-app-config.yaml defines the JQ mappings — no hardcoded field names in the Python code:
|
|
Running an Ocean Integration
|
|
Port vs. Backstage: Honest Comparison
Both tools solve the same problem but make fundamentally different trade-offs.
Philosophy
| Aspect | Port | Backstage |
|---|---|---|
| Type | SaaS product | Open-source framework |
| Deployment | Port-hosted | Self-hosted (you manage it) |
| Data model | Flexible, UI/API-driven | YAML files in repos (catalog-info.yaml) |
| Extensibility | Configuration-first | Code-first (plugins) |
| Infrastructure cost | SaaS subscription | Compute + PostgreSQL + maintenance |
Feature-by-Feature
Software Catalog
Both handle this well. Backstage uses catalog-info.yaml files committed alongside code — the catalog is Git-backed by default, which some teams strongly prefer for auditability. Port uses integrations and API pushes — the catalog is dynamic and can reflect real-time state (current pod counts, active incidents) rather than just what was committed.
Self-Service / Scaffolding
Backstage’s Software Templates are purpose-built for scaffolding: define a Nunjucks template, wire up a multi-step wizard, and Backstage creates files, repos, and catalog entries. It’s polished for the “create a new service” use case specifically.
Port’s actions are more general-purpose. You’re wiring Port’s UI to your existing GitHub Actions, Jenkins pipelines, or webhooks. More flexibility, slightly more setup. Port doesn’t have a built-in template rendering engine — you bring your own scaffolding logic in the workflow.
Documentation (TechDocs)
Backstage wins here. TechDocs is a first-class feature: point Backstage at a mkdocs.yml, and it renders your docs inside the portal. Engineers write Markdown, Backstage handles the rest.
Port supports Markdown properties and embedded URLs, but there’s no equivalent to TechDocs. Documentation lives outside Port; you link to it.
Scorecards
Port wins. Scorecards are native, configurable, and automatically evaluated against live catalog data. Backstage requires third-party plugins (Cortex, Roadie) or custom development for equivalent functionality.
Kubernetes Visibility
Port wins in practice. The Kubernetes integration installs cleanly via Helm and syncs cluster resources in minutes. Backstage’s Kubernetes plugin requires more configuration and frequently needs TLS certificate adjustments to work in real clusters.
Setup Time
Port is significantly faster to get to value. A working catalog with GitHub + Kubernetes integrations can be done in a day. Backstage typically takes 6–12 months to reach the same level of customization and coverage, and requires ongoing maintenance as Backstage versions update.
Customization Ceiling
Backstage’s ceiling is higher. Because it’s a React/Node.js framework, you can build anything — custom UI components, novel backend integrations, arbitrary data flows. Port’s configurability is deep (custom blueprints, JQ transformations, custom Ocean integrations) but you’re constrained to Port’s data model and UI paradigms.
Cost
Backstage is open-source (free), but “free” hides the engineering cost of building and maintaining it. Port costs money directly (see pricing section), but has a predictable cost model and no infrastructure to maintain.
RBAC
Both support role-based access control. Port’s RBAC is configured through the UI and supports entity ownership-based permissions. Backstage’s permissions system is more complex to configure but more flexible for unusual access patterns.
When to Choose Port
- Your platform team is small (under 5 FTEs)
- You need a working portal in weeks, not months
- You want to avoid maintaining a React/Node.js app
- You value real-time catalog data (current incidents, live cluster state)
- You want native scorecards without additional plugins
- Budget is available for SaaS
When to Choose Backstage
- You have strong open-source requirements or data sovereignty constraints
- You need deep UI customization beyond what Port’s interface designer supports
- Documentation-as-code (TechDocs) is a core requirement
- You have a dedicated platform engineering team with React experience
- You need a feature that doesn’t exist in Port and won’t be custom-integrated
- The 200+ community plugins cover your specific tech stack
Getting Started
1. Create a Free Account
Go to app.getport.io. No credit card required. The free tier gives you up to 15 seats, 10,000 entities, and 500 automation runs per month.
2. Define Your Data Model
Port’s onboarding wizard offers pre-built templates for common stacks (Kubernetes, GitHub, cloud resources). Start with a template and customize, or start from scratch in the Builder.
Navigate to Builder in the left sidebar. You’ll see default blueprints Port created. Add properties, customize relations, and define your entity types.
3. Install Integrations
Go to Builder → Integrations and click Add Integration. For most integrations, choose “Hosted by Port” to have Port run the integration process:
- GitHub: Installs a GitHub App — authorize Port to read your org, configure
port-app-config.ymlor use the UI global config - Kubernetes: Generates a
helm installcommand pre-populated with your credentials - PagerDuty: OAuth flow — authorize Port to read your PagerDuty org
4. Verify the Catalog
After integrations run their initial sync (minutes for GitHub/PagerDuty, a few minutes for Kubernetes), check Catalog in the left sidebar. You should see entities appearing under your blueprints.
5. Create Your First Action
In Builder → Self-Service, click New Action. Walk through the wizard:
- Choose the blueprint (entity type) the action applies to
- Define input properties (the form developers fill out)
- Choose backend (GitHub, webhook, etc.)
- Configure the invocation payload
- Save and test
6. Create a Scorecard
In Builder, select a blueprint → click Scorecards tab → New Scorecard. Define your levels and add rules. Port evaluates all existing entities immediately and shows compliance across your catalog.
Managing Port as Code
For production environments, manage Port’s data model through Terraform or the API rather than clicking around the UI:
|
|
Port’s Terraform provider resource types: port_blueprint, port_action, port_scorecard, port_entity, port_team, port_webhook.
Real-World Use Cases
Software Catalog
The starting point for most Port deployments. Connect GitHub (repositories + teams), Kubernetes (cluster resources), and PagerDuty (on-call + incidents). Within hours you have:
- Every service listed with its repo URL, language, owning team, and on-call engineer
- Each service linked to its running Kubernetes deployments
- Current open incidents surfaced directly on the service page
- No more “who owns this?” searches across Slack, GitHub, and Confluence
Developer Self-Service: Day-2 Operations
Beyond scaffolding, Port shines for day-2 operations. Common action patterns:
- Scale a deployment: Developer selects a service, inputs desired replica count → GitHub Action calls
kubectl scaleand reports back - Trigger a deployment: Developer selects service + environment → GitHub Action runs the deployment pipeline
- Create a feature flag: Developer inputs flag name + rollout % → webhook calls LaunchDarkly API
- Add a DNS record: Developer inputs subdomain → Terraform action provisions in Route53 → entity created in catalog
- Create a database: Developer selects service → GitHub Action runs Terraform to provision RDS instance → links to service entity
Each of these replaces a ticket, a Slack message, or direct infrastructure access.
On-Call Management
With PagerDuty integrated:
- Every service shows its current on-call engineer and open incidents
- Scorecards flag services with more than N open incidents
- Dashboards show on-call load per team
- Actions let engineers acknowledge, escalate, or resolve incidents from Port without opening PagerDuty
DORA Metrics Dashboard
Connect GitHub (deployment events) and PagerDuty (incident data). Port’s DORA guide walks you through creating calculation properties for:
- Deployment Frequency: deployments per day/week aggregated from GitHub workflow runs
- Lead Time for Changes: time from PR open to deployment, from PR data
- Change Failure Rate: deployments followed by incidents within N hours
- Mean Time to Recovery: incident open-to-resolve duration from PagerDuty
Create a scorecard against official DORA benchmarks (Low/Medium/High/Elite). Build a dashboard showing DORA scores per team. Share with leadership as a live engineering intelligence report.
Production Readiness Reviews
Before a service goes to production, use a scorecard to enforce:
- Has README (Bronze)
- Has owning team (Bronze)
- Branch protection enabled (Silver)
- Code owners defined (Silver)
- On-call schedule exists (Gold)
- Fewer than 3 open Snyk high-severity findings (Gold)
- Deployed within 30 days (Gold)
Services that don’t meet Gold can’t be promoted to production — enforced via a Port automation that fires when a service’s tier is set to tier-1 but its scorecard level is below Silver.
Pricing
Port’s pricing is per-seat (users who log into the portal):
| Tier | Price | Seats | Entities | Automation Runs |
|---|---|---|---|---|
| Free | $0 | Up to 15 | Up to 10,000 | 500/month |
| Basic | ~$30/seat/mo | Up to 50 | Up to 50,000 | Standard |
| Standard | ~$40/seat/mo | Up to 200 | Up to 250,000 | Up to 2,000/month |
| Enterprise | Custom | Unlimited | Custom (1M+) | Custom |
Key Free tier details:
- No credit card required, no expiration
- Full platform access (blueprints, actions, scorecards, all integrations)
- Community support via Slack and documentation
- 10,000 entities is enough for a medium-sized org: hundreds of services, repos, pipelines, and K8s resources
Standard adds SSO (SAML/OIDC), dynamic permissions (entity-level RBAC), and 5 workspaces (isolated environments for different teams or stages).
Enterprise adds SCIM provisioning (auto-sync users from Okta/Azure AD), Private Link, IP allowlisting, and 99.9% SLA with 4-hour critical support response.
Entity count is the main scaling constraint to watch. A single Kubernetes cluster with 200 services, 400 deployments, 600 pods, 10 nodes, and 20 namespaces generates ~1,230 entities. A large org with 10 clusters, 500 services, and full PagerDuty + GitHub sync can push toward 50,000 entities.
Limitations and Gotchas
Data sovereignty: Port is SaaS — your entity data lives in Port’s infrastructure. If your security posture requires on-premises data storage, Port isn’t the right fit (Backstage self-hosted is).
JQ learning curve: Port’s transformation layer is JQ throughout. If your team isn’t familiar with JQ, there’s a learning curve for writing selectors and mappings. Port’s UI helps, but complex transformations require JQ knowledge.
Static rule values in scorecards: Scorecard rules compare properties against static values only — you can’t write property_a > property_b. Work around this with calculation properties that compute the comparison result as a boolean.
GitHub workflow input limit: GitHub’s workflow_dispatch trigger supports maximum 10 inputs. For actions requiring more parameters, bundle them into a single JSON object input.
Scorecard identifier immutability: Once you create a scorecard with a given identifier, you can’t change it. Renaming requires creating a new scorecard and deleting the old one.
Integration sunset: The legacy GitHub integration sunsets July 15, 2026. If you’re running it, migrate to the Ocean-powered GitHub integration before then.
Summary
Port solves the “build vs. buy” dilemma for internal developer portals decisively: most organizations should not build and maintain a custom Backstage deployment unless they have a specific reason to. Port’s blueprint system is flexible enough to model any engineering organization’s reality, and the Ocean integration framework covers essentially everything in a modern tech stack.
The free tier is genuinely useful — 10,000 entities and full platform access means you can build a real, production-worthy catalog for a mid-sized organization without spending anything. The SaaS model means zero infrastructure overhead: no Backstage React app to keep current, no PostgreSQL to manage, no Node.js deployment pipeline to maintain.
The trade-off is real: Port is not open-source, and your data lives in Port’s cloud. For organizations with strict data sovereignty requirements, Backstage remains the answer. For everyone else, Port is the fastest path to a working, maintained internal developer portal that developers actually use.
Start with app.getport.io, connect your GitHub integration, and you’ll have a live service catalog in under an hour. The blueprint builder and scorecard system make it easy to grow from there into a full platform engineering hub.
Comments