r8s

Stop writing YAML. Start composing infrastructure.

The Problem

You have a microservice. It needs a Deployment, a Service, routing with TLS, and a PostgreSQL database. That's 300+ lines of YAML boilerplate. Copy-paste between services. Hope you didn't miss an indentation.

❌ Raw YAML — Copy-paste hell

❌ Helm — values.yaml sprawl

❌ Kustomize — Can't abstract logic

The Solution

One component. Three resources. All wired together with sensible defaults.

TSX
import { App } from '@r8s/recipes';

export default () => (
  <App
    name="api"
    image="myapp/api:v1.2.3"
    host="api.example.com"
  />
);
YAML
$ npx r8s render
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api
---
apiVersion: v1
kind: Service
metadata:
  name: api
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: api-endpoint
# ... 3 resources rendered from 1 component

Why r8s?

DRY by Default

Your 10 microservices all need the same database setup? One component, imported everywhere.

import { App, Database } from '@r8s/recipes';

export default () => (
  <>
    <Database name="api-db" storage="10Gi" />
    <App
      name="api"
      image="myapp/api:v1.2.3"
      host="api.example.com"
    />
  </>
);

Type Safety Out of the Box

Misspelled containerPort? TypeScript catches it. Wrong apiVersion? Red squiggly. No more kubectl apply → "error: validation failed."

function App({ environment }: { environment: 'staging' | 'production' }) {
  const replicas = environment === 'production' ? 3 : 1;

  return (
    <deployment
      metadata={{ name: 'api' }}
      spec={{
        replicas,
        template: {
          spec: {
            containers: [{
              name: 'api',
              image: 'myapp/api:v1',
            }],
          },
        },
      }}
    />
  );
}

Real Code, Real Logic

Use conditionals, loops, and variables. Environment-specific configuration without template syntax nightmares.

Testable Infrastructure

Because r8s components are just TypeScript functions, you can test them with standard tools like Vitest.

import { describe, it, expect } from 'vitest';
import { render } from '@r8s/core';
import { App } from '@r8s/recipes';

describe('MyApp', () => {
  it('should create a Deployment with 3 replicas', () => {
    const result = render(
      <App name="api" image="myapp/api:v1" host="api.example.com" replicas={3} />
    );

    const deployment = result.resources.find(r => r.kind === 'Deployment');
    expect(deployment.spec.replicas).toBe(3);
  });
});

Quick Start

01

Create a new project

npx r8s init my-project
cd my-project
npm install
02

Edit your components

import { App } from '@r8s/recipes';

export default () => (
  <App
    name="myapp"
    image="myapp/web:v1.2.3"
    host="myapp.example.com"
    replicas={3}
  />
);
03

Render to YAML

npx r8s render --out k8s/manifest.yaml

Deploy with AI agents

r8s is designed for LLM-driven workflows. Point your agent at r8s.berget.ai/llms.txt — it contains the quick-start, CLI reference, and the component model. The agent writes TSX, renders with the r8s CLI, and hands plain YAML to your pipeline.

PROMPT
Read https://r8s.berget.ai/llms.txt, then:

Create an r8s project for my API:
- name: api, image: ghcr.io/myorg/api:v1.2.3
- host: api.example.com, 3 replicas, port 3000
- PostgreSQL database (20Gi)

Render to k8s/manifest.yaml.

Comparison

Raw YAMLHelmKustomizer8s
Composition❌ Copy-paste⚠️ Templates❌ Patches only✅ Components
Type Safety✅ Full TS
DRY⚠️ Values files⚠️ Bases✅ Import & reuse
Learning CurveLowMediumLowLow
GitOps Friendly✅ Yes