Back to Blog
Platform EngineeringDevOpsDeveloper ExperienceBackstageGitOpsKubernetes

Building a Platform Engineering Team from Scratch

When you need a platform team, what it should not own, the first three hires, one golden path from template repo to GitOps deploy, and how to measure it.

P
Davi Nunes
March 13, 2026Updated October 30, 202611 min read

Platform teams usually fail in one of two ways. An operations group is renamed, keeps its ticket queue, and nothing else changes. Or a new team spends its first quarter installing a portal that shows the existing mess in a cleaner interface.

This guide takes the other route: decide whether you need a platform team, write down what it owns and what it does not, hire three people, build one golden path end to end, and measure whether it made anything shorter.

Signals That You Need a Platform Team

The signals are repetition and waiting:

  • The same infrastructure exists in drifting copies. Each team keeps its own pipeline, Dockerfile, manifests and Terraform. A base image update becomes one merge request per repository, and some never merge.
  • Starting a service means waiting. The code takes a day; the namespace, secrets, DNS record and registry access take weeks of requests to different people.
  • The same questions come up every week. How do I get a database, why is my deploy stuck, who can give me access. The answers live in two people's heads.
  • Senior product engineers spend real time on plumbing. Ask how many hours last week went on pipelines and clusters, then multiply by the number of teams.
  • An audit needs uniform evidence. ISO 27001, SOC 2 and NIS2 all ask you to show every service gets the same controls, and the only proof today is reading every repository.

To put a number on it, take the last change that had to reach every service, such as a base image bump. Count its merge requests and the days until the last one reached production. A platform team turns that into one change in one place.

Signals that you need something smaller:

  • One or two product teams and a single deployable. You need a solid shared pipeline and possibly one more SRE, not a team with a charter.
  • The pain is "nobody knows how", not "it takes too long". That is a documentation problem. Write the runbook first.
  • The pain is incidents and on-call load. That is reliability work: SLOs, alerting, postmortems. A platform team will not quiet a noisy pager.

Write the Charter Before You Hire

A charter is one page: what the team owns, what it does not, and how to reach it. Written first, it stops the team becoming the home of every unowned task.

Start with a mission a product engineer can test: a team can go from an empty repository to a service running in production, observable and on the standard deployment path, without opening a ticket.

The platform team owns:

  • Service templates, the shared pipeline definition and the base container images, including their patch cadence.
  • The deployment path: the GitOps repository, the GitOps controller and the cluster add-ons every service depends on.
  • Observability defaults: metrics scraping, log shipping, trace export, a standard dashboard and alert set per service.
  • The platform's own reliability, with its own SLOs and on-call rotation.

Just as important, it does not own:

  • Application code, application alerts or on-call for product services. It supports those incidents; it does not run them.
  • One-off infrastructure outside the golden path. "Can you just create me a VM" becomes a backlog conversation, not a ticket executed quietly.
  • Each team's choice of language or framework.
  • A product team's cloud spend. The platform makes cost visible per service; the team owns the number.

Close with where requests go and where the roadmap lives. If you bring in outside help with platform engineering, get this page right first: every later decision is checked against it.

The First Three Hires

Do not start with one person. A solo platform engineer becomes the ticket queue almost immediately. Three is the smallest team that can put one person on interrupt duty each week while two build.

  1. 1A lead who has run production systems and can say no. Senior, fluent in Kubernetes, CI and infrastructure as code, and willing to be the platform's product manager: interviews, roadmap, charter.
  2. 2An infrastructure engineer. Clusters, networking, the GitOps controller, Terraform or OpenTofu, secrets. The person who makes the path reliable.
  3. 3A developer-experience engineer from a product team. Someone who has written services in your main language and felt the pain as a user. They build the template, docs and scripts, and notice what is easy for an operator but baffling for a developer.

Growing the team from inside has a clear advantage: an engineer from a product team knows the codebase and the people, and arrives with credibility. The cost: that team loses a senior person, and one team's habits risk becoming everyone's standard. A middle option is a rotation: product engineers spend a cycle in the platform team, then return carrying context and feedback.

The Thinnest Viable Platform: One Golden Path, End to End

Pick your most common service type, usually an HTTP API in your main language, and build one path for it from an empty repository to production. Nothing else gets built until a real team has shipped through it.

The path has five pieces: a template repository, a shared pipeline, an image build, a GitOps deployment and observability defaults. Laid out across two repositories:

text
service-template/                # copied once per new service
├── Dockerfile                   # multi-stage, non-root user, pinned base image
├── catalog-info.yaml            # owner and lifecycle, for a catalog later
├── README.md                    # run, test, deploy: in that order
├── (CI entry file)              # one include of the shared pipeline, pinned to a tag
├── src/                         # hello-world handler serving /healthz and /metrics
└── test/

deployments/                     # one repository, watched by Argo CD
├── staging/
│   └── orders-api/
└── production/
    └── orders-api/              # adding this directory is the deploy request
        ├── kustomization.yaml   # image tag, bumped by the pipeline
        ├── deployment.yaml      # probes, requests and limits, securityContext
        ├── service.yaml
        └── servicemonitor.yaml  # Prometheus scraping, on by default

The shared pipeline lives in its own repository, versioned with tags so teams upgrade on purpose. Its stages are the same for every service: test, build the image tagged with the commit SHA, scan, push, then run kustomize edit set image in the service's staging directory and commit. Promotion to production is a merge request doing the same in the production directory, so every deploy has a reviewable, revertible record. For hardening that pipeline, see our guide to a secure CI/CD pipeline with GitOps.

The piece that removes the ticket is an ApplicationSet. Instead of an Argo CD Application created by hand per service, one manifest turns every directory under production/ into a deployed application:

yaml
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: services-production
  namespace: argocd
spec:
  goTemplate: true
  generators:
    - git:
        repoURL: https://git.example.com/platform/deployments.git
        revision: main
        directories:
          - path: production/*
  template:
    metadata:
      name: '{{.path.basename}}-production'
    spec:
      project: default
      source:
        repoURL: https://git.example.com/platform/deployments.git
        targetRevision: main
        path: '{{.path.path}}'
      destination:
        server: https://kubernetes.default.svc
        namespace: '{{.path.basename}}'
      syncPolicy:
        automated:
          prune: true
          selfHeal: true
        syncOptions:
          - CreateNamespace=true

A second ApplicationSet does the same for staging/. In a real setup, replace project: default with an Argo CD project that limits where each service may deploy. Our GitOps consulting covers this layout and the promotion flow in depth.

Observability comes from the template. The handler exposes /metrics and the ServiceMonitor scrapes it. The template configures the OpenTelemetry SDK from the standard OTEL_SERVICE_NAME and OTEL_EXPORTER_OTLP_ENDPOINT variables, which the platform injects. Logs go to stdout as JSON, and a dashboard and alert set keyed on the service name exist from the first deploy.

Done is a test, not a demo. In the hands-off onboarding test, a product engineer who has never seen the path goes from nothing to a production deploy while the platform team watches without touching the keyboard. Every question they ask is a gap in the docs or the template. Fix them, then repeat with someone else.

Not sure which path to pave first? Request a free platform engineering assessment. We walk one of your services from commit to production, time every hand-off and wait along the way, and tell you which golden path would remove the most of them.

Run the Platform as a Product

Developers are the platform's customers, and they have an alternative: doing it themselves, as they do today.

  • Talk to users on a schedule. Shadow a new joiner's first week. Sit with a team creating a service. Hold weekly office hours. Read the platform chat channel as research.
  • Keep a public roadmap. Now, next and later, visible to everyone, plus what you declined and why.
  • Earn adoption. The paved path must be the easiest way to ship. Teams may leave it, but leaving means owning what the platform did for them: base image patches, pipeline upkeep, the deploy mechanism. When a team routes around the platform, treat it as a finding about the platform.
  • Write the deprecation policy before the first deprecation. Announce with a removal date, keep old and new working side by side during an overlap window, and ship a migration guide or, better, a merge request that does the migration. Never remove something a production service depends on without that team's agreement.

Measure It

Take baselines before the platform ships anything, or you cannot show an after.

DORA's four keys (the delivery metrics, not the EU regulation): deployment frequency, lead time for changes, change failure rate and time to restore service. With the layout above, most come out of Git. Production deploys per ISO week are the commits that touched production/:

bash
git -C deployments log --since="12 weeks ago" --date=format:'%G-W%V' \
  --format='%ad' -- production/ | sort | uniq -c

Lead time runs from a commit in a service repository to the production commit that deployed it; the image tag is the commit SHA, so a short script joins the two. Change failure rate is the share of production commits followed by a revert or hotfix, or linked to an incident. Time to restore comes from your incident tracker, opened to resolved.

Time from a new repository to its first production deploy. Compare the first commit in the service repository with the first commit to its production directory:

bash
first_commit=$(git -C orders-api log --reverse --format=%cI | head -1)
first_deploy=$(git -C deployments log --reverse --format=%cI -- production/orders-api/ | head -1)
echo "$first_commit -> $first_deploy"

Onboarding time for a new engineer. Days from start date to their first change in production: the date from HR, the change from the first commit with their author email that shipped in a production deploy. After week one, ask what blocked them.

Ticket volume to the platform team. Label every request with a category: access, new infrastructure, broken deploy, question. Count per category per week. A recurring category is a self-service candidate; a category that falls to zero after you automate it is your evidence.

Anti-Patterns

The ticket queue renamed "platform". Same people, same queue, new name. If the input is tickets and the output is closed tickets, it is an operations team. The way out: treat each recurring ticket category as a backlog item whose goal is making that ticket unnecessary.

A portal before any golden path. Installing Backstage first catalogs how things are today. A portal is a front door, worth building once something is behind it. Because the template ships a catalog-info.yaml, the catalog fills itself when you add one. Then Backstage consulting adds the catalog and templates on top of the path you already have.

Mandating the platform. A mandate produces compliance, not adoption. Teams follow the rule on paper, stop giving feedback, and hide their workarounds.

One size for everyone. Batch jobs, data pipelines and GPU workloads do not fit an HTTP service template. Pave the common path, document what leaving it means, and add a second path only when several teams need the same thing.

The First 90 Days

Month 1: charter, baselines, first path chosen.

  • The charter, agreed with engineering leadership and published.
  • Baselines for DORA's four keys, repository-to-production time, onboarding time and ticket categories.
  • Interviews with at least one engineer per product team, and a ranked list of hand-offs on the way to production.
  • A pilot team that has agreed to use the first golden path.

Month 2: the path built and used once.

  • Template repository, tagged shared pipeline, base image, deployments repository and ApplicationSets, default dashboards and alerts.
  • The pilot team ships a real service to production through the path.
  • The hands-off onboarding test run once, and its findings fixed.

Month 3: the path used without help.

  • Two more teams on the path, onboarded from the docs alone.
  • Public roadmap and deprecation policy published, office hours running.
  • A first review against the baselines, numbers as they are, and a decision on what is next: a second path, self-service databases, or a portal.

If you are about to staff a platform team, or yours has turned into a ticket queue, book a free platform engineering assessment. We review your charter or help you write one, measure your current time from repository to production, and identify the first golden path worth building.