Skip to content
Back to blog Port and Kratix: Internal Developer Platforms Beyond Backstage

Port and Kratix: Internal Developer Platforms Beyond Backstage

Platform EngineeringDevOps

Port and Kratix: Internal Developer Platforms Beyond Backstage

Backstage is a developer portal. Port and Kratix go further - they’re platforms for building platforms. Port focuses on the catalog and self-service actions. Kratix focuses on composable infrastructure delivery.

This guide covers when to use each and how to implement them.

TL;DR

  • Port: SaaS developer portal with actions and scorecards
  • Kratix: Self-hosted platform framework with Promises
  • Backstage for catalog + docs, Port for actions + metrics
  • Kratix for GitOps-native infrastructure delivery
  • All can work together

Port: Self-Service Developer Portal

Port is a SaaS platform for building developer portals with self-service capabilities.

Architecture

┌─────────────────────────────────────────────────────────────────┐
│                           Port                                   │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────────┐  │
│  │  Software   │  │  Self-Svc   │  │     Scorecards          │  │
│  │  Catalog    │  │  Actions    │  │  (Production Readiness) │  │
│  └─────────────┘  └─────────────┘  └─────────────────────────┘  │
└─────────────────────────────────────────────────────────────────┘

          ┌────────────────────┼────────────────────┐
          ▼                    ▼                    ▼
   ┌────────────┐       ┌────────────┐       ┌────────────┐
   │  GitHub    │       │ Kubernetes │       │   Slack    │
   │            │       │            │       │            │
   └────────────┘       └────────────┘       └────────────┘

Define Blueprints

{
  "identifier": "service",
  "title": "Service",
  "icon": "Microservice",
  "schema": {
    "properties": {
      "language": {
        "type": "string",
        "enum": ["Go", "Python", "Node.js", "Java"]
      },
      "tier": {
        "type": "string",
        "enum": ["critical", "standard", "experimental"]
      },
      "owner": {
        "type": "string"
      },
      "repository": {
        "type": "string",
        "format": "url"
      },
      "slackChannel": {
        "type": "string"
      },
      "onCall": {
        "type": "string"
      },
      "productionReadiness": {
        "type": "number",
        "minimum": 0,
        "maximum": 100
      }
    },
    "required": ["language", "tier", "owner"]
  },
  "relations": {
    "environment": {
      "target": "environment",
      "many": true
    },
    "dependencies": {
      "target": "service",
      "many": true
    }
  }
}

Self-Service Actions

{
  "identifier": "create_service",
  "title": "Create New Service",
  "icon": "Plus",
  "trigger": {
    "type": "self-service",
    "userInputs": {
      "properties": {
        "name": {
          "type": "string",
          "pattern": "^[a-z][a-z0-9-]*$"
        },
        "language": {
          "type": "string",
          "enum": ["Go", "Python", "Node.js"]
        },
        "tier": {
          "type": "string",
          "enum": ["critical", "standard"]
        },
        "includeDatabase": {
          "type": "boolean",
          "default": false
        }
      },
      "required": ["name", "language", "tier"]
    }
  },
  "invocationMethod": {
    "type": "GITHUB",
    "org": "company",
    "repo": "platform-actions",
    "workflow": "create-service.yaml"
  }
}

GitHub Action Backend

# .github/workflows/create-service.yaml
name: Create Service
on:
  workflow_dispatch:
    inputs:
      name:
        required: true
      language:
        required: true
      tier:
        required: true
      includeDatabase:
        required: false
        default: 'false'
      port_run_id:
        required: true

jobs:
  create:
    runs-on: ubuntu-latest
    steps:
      - name: Notify Port - Running
        uses: port-labs/port-github-action@v1
        with:
          clientId: ${{ secrets.PORT_CLIENT_ID }}
          clientSecret: ${{ secrets.PORT_CLIENT_SECRET }}
          runId: ${{ inputs.port_run_id }}
          status: "RUNNING"

      - name: Create Repository
        uses: actions/github-script@v6
        with:
          script: |
            await github.rest.repos.createUsingTemplate({
              template_owner: 'company',
              template_repo: '${{ inputs.language }}-service-template',
              name: '${{ inputs.name }}',
              owner: 'company',
              private: true
            })

      - name: Create Database (if requested)
        if: inputs.includeDatabase == 'true'
        run: |
          # Trigger Crossplane claim or Terraform
          kubectl apply -f - <<EOF
          apiVersion: database.platform.company.com/v1alpha1
          kind: PostgreSQLInstance
          metadata:
            name: ${{ inputs.name }}-db
            namespace: platform
          spec:
            size: small
          EOF

      - name: Register in Port
        uses: port-labs/port-github-action@v1
        with:
          clientId: ${{ secrets.PORT_CLIENT_ID }}
          clientSecret: ${{ secrets.PORT_CLIENT_SECRET }}
          operation: CREATE
          blueprint: service
          identifier: ${{ inputs.name }}
          properties: |
            {
              "language": "${{ inputs.language }}",
              "tier": "${{ inputs.tier }}",
              "repository": "https://github.com/company/${{ inputs.name }}"
            }

      - name: Notify Port - Complete
        uses: port-labs/port-github-action@v1
        with:
          clientId: ${{ secrets.PORT_CLIENT_ID }}
          clientSecret: ${{ secrets.PORT_CLIENT_SECRET }}
          runId: ${{ inputs.port_run_id }}
          status: "SUCCESS"
          summary: "Service ${{ inputs.name }} created successfully"

Scorecards

{
  "identifier": "production_readiness",
  "title": "Production Readiness",
  "rules": [
    {
      "identifier": "has_readme",
      "title": "Has README",
      "level": "Bronze",
      "query": {
        "property": "hasReadme",
        "operator": "=",
        "value": true
      }
    },
    {
      "identifier": "has_monitoring",
      "title": "Has Monitoring",
      "level": "Silver",
      "query": {
        "property": "hasMonitoring",
        "operator": "=",
        "value": true
      }
    },
    {
      "identifier": "has_runbook",
      "title": "Has Runbook",
      "level": "Silver",
      "query": {
        "property": "runbookUrl",
        "operator": "isNotEmpty"
      }
    },
    {
      "identifier": "slo_defined",
      "title": "SLO Defined",
      "level": "Gold",
      "query": {
        "property": "sloAvailability",
        "operator": ">",
        "value": 0
      }
    }
  ]
}

Kratix: Composable Platform Framework

Kratix lets you define “Promises” - self-service capabilities that developers can request. It’s GitOps-native and works with any Kubernetes resources.

Architecture

┌─────────────────────────────────────────────────────────────────┐
│                      Platform Cluster                            │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │                    Kratix Controller                         ││
│  │  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────┐  ││
│  │  │   Promise   │  │   Promise   │  │      Promise        │  ││
│  │  │  (Database) │  │  (Logging)  │  │  (Environment)      │  ││
│  │  └─────────────┘  └─────────────┘  └─────────────────────┘  ││
│  └─────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────┘

                               ▼ GitOps (Flux/Argo)
          ┌────────────────────┼────────────────────┐
          ▼                    ▼                    ▼
   ┌────────────┐       ┌────────────┐       ┌────────────┐
   │  Worker 1  │       │  Worker 2  │       │  Worker 3  │
   │  (Dev)     │       │  (Staging) │       │  (Prod)    │
   └────────────┘       └────────────┘       └────────────┘

Define a Promise

# promise-postgresql.yaml
apiVersion: platform.kratix.io/v1alpha1
kind: Promise
metadata:
  name: postgresql
spec:
  # What developers request
  api:
    apiVersion: apiextensions.k8s.io/v1
    kind: CustomResourceDefinition
    metadata:
      name: postgresqls.database.platform.company.com
    spec:
      group: database.platform.company.com
      names:
        kind: PostgreSQL
        plural: postgresqls
        singular: postgresql
      scope: Namespaced
      versions:
        - name: v1
          served: true
          storage: true
          schema:
            openAPIV3Schema:
              type: object
              properties:
                spec:
                  type: object
                  properties:
                    size:
                      type: string
                      enum: ["small", "medium", "large"]
                    version:
                      type: string
                      default: "15"
                  required:
                    - size
  
  # Pipeline to process requests
  workflows:
    resource:
      configure:
        - apiVersion: platform.kratix.io/v1alpha1
          kind: Pipeline
          metadata:
            name: configure-postgresql
          spec:
            containers:
              - name: generate-manifests
                image: company/postgresql-pipeline:latest
                command:
                  - /bin/sh
                  - -c
                  - |
                    # Read request
                    SIZE=$(yq '.spec.size' /kratix/input/object.yaml)
                    VERSION=$(yq '.spec.version' /kratix/input/object.yaml)
                    NAME=$(yq '.metadata.name' /kratix/input/object.yaml)
                    NAMESPACE=$(yq '.metadata.namespace' /kratix/input/object.yaml)
                    
                    # Map size to resources
                    case $SIZE in
                      small)  CPU=500m; MEM=1Gi; STORAGE=10Gi ;;
                      medium) CPU=1;    MEM=2Gi; STORAGE=50Gi ;;
                      large)  CPU=2;    MEM=4Gi; STORAGE=100Gi ;;
                    esac
                    
                    # Generate CloudNativePG cluster
                    cat > /kratix/output/cluster.yaml <<EOF
                    apiVersion: postgresql.cnpg.io/v1
                    kind: Cluster
                    metadata:
                      name: ${NAME}
                      namespace: ${NAMESPACE}
                    spec:
                      instances: 3
                      imageName: ghcr.io/cloudnative-pg/postgresql:${VERSION}
                      storage:
                        size: ${STORAGE}
                      resources:
                        requests:
                          cpu: ${CPU}
                          memory: ${MEM}
                      monitoring:
                        enablePodMonitor: true
                    EOF

Developer Usage

# database-request.yaml
apiVersion: database.platform.company.com/v1
kind: PostgreSQL
metadata:
  name: my-app-db
  namespace: my-team
spec:
  size: medium
  version: "15"

Multi-Cluster Delivery

# kratix-destination.yaml
apiVersion: platform.kratix.io/v1alpha1
kind: Destination
metadata:
  name: production
spec:
  stateStoreRef:
    name: production-gitops
    kind: GitStateStore
  strictMatchLabels: true
  
---
apiVersion: platform.kratix.io/v1alpha1
kind: GitStateStore
metadata:
  name: production-gitops
spec:
  url: https://github.com/company/gitops-production
  branch: main
  secretRef:
    name: github-credentials

Comparison

FEATURE                 PORT            KRATIX          BACKSTAGE
=======                 ====            ======          =========
Hosting                 SaaS            Self-hosted     Self-hosted
Catalog                 ✅              ❌              ✅
Self-Service Actions    ✅ (built-in)   ✅ (Promises)   ✅ (plugins)
Scorecards              ✅              ❌              ✅ (plugins)
GitOps Native           ✅ (GitHub)     ✅              ❌
Infrastructure          Actions         Native          Plugins
Learning Curve          Low             Medium          High
Customization           Medium          High            Very High

When to Use Each

Port:

  • Quick time-to-value
  • Non-technical stakeholders need visibility
  • Scorecards and compliance tracking
  • Existing GitHub/GitLab workflows

Kratix:

  • GitOps-native infrastructure delivery
  • Multi-cluster deployments
  • Complex infrastructure composition
  • Want to own the platform

Backstage:

  • Need extensive customization
  • Strong frontend development capability
  • Want to own the entire portal

References

======================================== Port + Kratix + Platform Engineering

Self-service infrastructure. Developer happiness.

Found this helpful?

Comments