Getting Started

From zero to production-ready in 5 steps

Step 1

Your First App

One component. Three resources. Deployment, Service, and Ingress — all wired together.

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

export default () => (
  <App
    name="myapp"
    image="nginx:latest"
    host="myapp.example.com"
  />
);
YAML
# 3 resources created:
# - Deployment (myapp)
# - Service (myapp)
# - Ingress (myapp-endpoint)

Run npx r8s render to see the full YAML output.

Step 2

Add a Database

Add a PostgreSQL database with one line. CNPG creates the cluster, manages credentials, and auto-wires the connection string to your app.

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

export default () => (
  <>
    <Database name="myapp-db" storage="10Gi" />
    <App
      name="myapp"
      image="myapp:v1"
      host="myapp.example.com"
    />
  </>
);
YAML
# 5 resources created:
# - Cluster (myapp-db) — CNPG PostgreSQL
# - Deployment (myapp)
# - Service (myapp)
# - Ingress (myapp-endpoint)
# - Secret (myapp-db-app) — auto-generated credentials

The DATABASE_URL environment variable is automatically set in your Deployment.

Step 3

Add Monitoring

Prometheus scraping with one component. The operator is declared automatically.

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

export default () => (
  <>
    <Database name="myapp-db" storage="10Gi" />
    <App
      name="myapp"
      image="myapp:v1"
      host="myapp.example.com"
    />
    <Monitoring
      name="myapp-monitor"
      selector={{ app: 'myapp' }}
    />
  </>
);
# 7 resources created:
# - Cluster (myapp-db)
# - Deployment (myapp)
# - Service (myapp)
# - Ingress (myapp-endpoint)
# - Secret (myapp-db-app)
# - ServiceMonitor (myapp-monitor) — Prometheus scraping
# - Prometheus Operator — declared as dependency
Step 4

Add Providers

Wrap your app in providers for secrets, DNS, and routing. Each provider declares its required operators.

import { SecretProvider, DnsProvider, EndpointProvider, App, Database, Monitoring } from '@r8s/recipes';

export default () => (
  <SecretProvider provider="openbao">
    <DnsProvider provider="external-dns">
      <EndpointProvider provider="nginx">
        <Database name="myapp-db" storage="10Gi" />
        <App
          name="myapp"
          image="myapp:v1"
          host="myapp.example.com"
        />
        <Monitoring
          name="myapp-monitor"
          selector={{ app: 'myapp' }}
        />
      </EndpointProvider>
    </DnsProvider>
  </SecretProvider>
);
# 9 resources created:
# - Cluster (myapp-db)
# - Deployment (myapp)
# - Service (myapp)
# - Ingress (myapp-endpoint)
# - OpenBaoStaticSecret (myapp-db-app) — synced from OpenBao
# - ServiceMonitor (myapp-monitor)
# - DNSEndpoint (myapp-endpoint-dns) — automatic DNS record
# - Prometheus Operator
# - ExternalDNS Operator
# - Vault Secrets Operator

Now your secrets come from OpenBao, DNS records are created automatically, and routing uses nginx with TLS.

Step 5

Production Ready

Full production setup: Keycloak for auth, Redis for cache, Loki for logs, TSIG for DNS, and TLS everywhere. All operators declared, all secrets managed.

import { SecretProvider, OpenBao, DnsProvider, ExternalDns, EndpointProvider, Nginx, App, Database, Monitoring, Auth } from '@r8s/recipes';

export default () => (
  <SecretProvider provider={<OpenBao mount="secret" path="myapp" />}>
    <DnsProvider provider={<ExternalDns server="ns1.example.com" tsig={{ path: 'dns/tsig', key: 'secret' }} />}>
      <EndpointProvider provider={<Nginx tls={{ clusterIssuer: 'letsencrypt-prod' }} />}>
        <Database name="myapp-db" storage="20Gi" />
        <Auth name="auth" host="auth.example.com" />
        <App
          name="myapp"
          image="myapp:v1"
          host="myapp.example.com"
          replicas={3}
          cache
        />
        <Monitoring
          name="myapp-monitor"
          selector={{ app: 'myapp' }}
          logs
        />
      </EndpointProvider>
    </DnsProvider>
  </SecretProvider>
);
# 15+ resources created:
# - Cluster (myapp-db) — CNPG PostgreSQL
# - Keycloak (auth) — Identity provider
# - Cluster (auth-db) — Keycloak's database
# - RedisCluster (myapp-cache) — Session store
# - Deployment (myapp) — 3 replicas
# - Service (myapp)
# - Ingress (myapp-endpoint) — TLS via cert-manager
# - Ingress (auth-endpoint) — TLS via cert-manager
# - OpenBaoStaticSecret (myapp-db-app)
# - OpenBaoStaticSecret (auth-db-app)
# - OpenBaoStaticSecret (external-dns-tsig) — TSIG for DNS updates
# - ServiceMonitor (myapp-monitor)
# - LokiStack (myapp-monitor-loki) — Log aggregation
# - Logging (myapp-monitor-logging)
# - Flow (myapp-monitor-flow)
# - Output (myapp-monitor-loki-output)
# - DNSEndpoint (myapp-endpoint-dns)
# - DNSEndpoint (auth-endpoint-dns)
# - All operators declared automatically

This is a complete production stack — auth, database, cache, monitoring, logging, DNS, and TLS — in ~30 lines of TypeScript.

What's next?