Web Application Compositions
Higher-level compositions that wire together CNPG PostgreSQL, Valkey cache, Inngest workflow engine, and your application deployment. All connection strings are automatically injected as environment variables.
Import
import { makeWebAppWithProcessing, webAppWithProcessing } from 'typekro/webapp';webAppWithProcessing
Deploys a complete application stack with automatic wiring:
| Component | Resource | Wired env var |
|---|---|---|
| PostgreSQL | CNPG Cluster + PgBouncer Pooler | DATABASE_URL |
| Cache | Valkey cluster | VALKEY_URL, REDIS_URL |
| Workflow engine | Inngest (external DB mode) | INNGEST_BASE_URL; credentials are injected from a generated Secret via envFrom |
| Application | Deployment + Service | — |
Quick Example
import { webAppWithProcessing } from 'typekro/webapp';
// 'kro' = continuous reconciliation, 'direct' = immediate apply
const factory = webAppWithProcessing.factory('kro', {
namespace: 'production',
waitForReady: true,
});
const instance = await factory.deploy({
name: 'my-app',
namespace: 'production',
app: {
image: 'my-app:latest',
port: 3000,
replicas: 2,
env: {
NODE_ENV: 'production',
},
},
database: {
instances: 3,
storageSize: '50Gi',
storageClass: 'gp3',
database: 'myapp',
},
cache: {
shards: 3,
replicas: 1,
},
processing: {
eventKey: 'your-hex-event-key',
signingKey: 'your-hex-signing-key',
sdkUrl: ['http://my-app:3000/api/inngest'],
},
});What gets deployed
Given name: 'my-app', the composition creates:
| Resource | Name | Type |
|---|---|---|
| PostgreSQL cluster | my-app-db | CNPG Cluster (3 instances) |
| Connection pooler | my-app-db-pooler | CNPG Pooler (PgBouncer, transaction mode) |
| Cache | my-app-cache | Valkey cluster |
| Workflow engine | my-app-inngest | Inngest (HelmRelease, external DB) |
| Application | my-app | Deployment + Service |
Environment variables injected into the app
DATABASE_URL=postgresql://app@my-app-db-pooler:5432/myapp
VALKEY_URL=redis://my-app-cache:6379
REDIS_URL=redis://my-app-cache:6379
INNGEST_BASE_URL=http://my-app-inngest:8288
INNGEST_EVENT_KEY=<from generated Secret>
INNGEST_SIGNING_KEY=<from generated Secret>The composition creates an Inngest credentials Secret and prepends it to app.envFrom for direct deployments. In KRO mode, the generated Inngest credentials Secret is still mounted, but schema-provided app.envFrom entries are not merged into the generated Deployment because KRO cannot safely concatenate typed list fields. User-provided app.env values are still merged on top of generated direct environment variables, so they can override direct values such as DATABASE_URL or INNGEST_BASE_URL.
In KRO mode, processing.eventKey and processing.signingKey are still plain custom-resource spec fields before TypeKro copies them into the generated Secret. Treat webapp custom resources as sensitive and restrict RBAC read access to those CRs.
Status
instance.status.ready // all components healthy
instance.status.databaseUrl // postgresql://app@...-db-pooler:5432/...
instance.status.databaseHost // ...-db-pooler
instance.status.databasePort // 5432
instance.status.cacheUrl // redis://...-cache:6379
instance.status.cacheHost // ...-cache
instance.status.cachePort // 6379
instance.status.inngestUrl // http://...-inngest:8288
instance.status.appUrl // http://...:3000
instance.status.components.app // app deployment ready
instance.status.components.database // CNPG cluster healthy
instance.status.components.cache // Valkey ready
instance.status.components.inngest // Inngest readyConfiguration
| Field | Required | Description |
|---|---|---|
name | Yes | App name (prefix for all resources) |
namespace | No | Target namespace (default: 'default') |
app.image | Yes | Container image |
app.port | No | Container port (default: 3000) |
app.replicas | No | Replica count (default: 1) |
app.env | No | Extra env vars (merged with auto-wired ones) |
app.envFrom | No | Additional Secret/ConfigMap envFrom sources for direct deployments. TypeKro prepends the generated Inngest credentials Secret, so later entries can override only by defining the same env keys in a later source according to Kubernetes envFrom behavior. In KRO mode, only the generated Inngest credentials Secret is mounted |
database.storageSize | Yes | PG storage (e.g., '50Gi') |
database.instances | No | PG replicas (default: 1) |
database.storageClass | No | Storage class |
database.database | No | Database name (default: app) |
database.owner | No | DB owner (default: 'app') |
cache.shards | No | Valkey shards (default: 3) |
cache.replicas | No | Replicas per shard (default: 0) |
cache.volumePermissions | No | Enable the Valkey volume permissions init container |
cache.storageSize | No | Storage size per Valkey shard (default: '1Gi') |
cache.storageClass | No | Storage class for Valkey PVCs (for example, gp3 or local-path) |
processing.eventKey | Yes | Inngest event key (hex string); sensitive in KRO custom-resource specs |
processing.signingKey | Yes | Inngest signing key (hex string); sensitive in KRO custom-resource specs |
processing.sdkUrl | No | App SDK URLs for function sync |
processing.replicas | No | Inngest server replicas (default: 1) |
processing.resources | No | CPU/memory requests and limits for the Inngest server |
cnpgOperator | No | CloudNativePG operator singleton settings; accepts the underlying CNPG bootstrap fields such as name, namespace, version, resources, customValues, and shared. name/namespace customize the install target; the singleton identity is fixed by the composition. |
Valkey operator migration and customization
Valkey operator installation is a build-time graph choice. Customize it with makeWebAppWithProcessing so singleton identity and ownership remain concrete when TypeKro generates a KRO graph:
import { makeWebAppWithProcessing } from 'typekro/webapp';
const internalWebApp = makeWebAppWithProcessing({
valkeyOperator: {
name: 'platform-valkey-operator',
namespace: 'platform-valkey-system',
version: 'v0.0.61',
values: { replicaCount: 2 },
},
});
const factory = internalWebApp.factory('kro', { namespace: 'production' });
// Deploy the same per-instance webapp spec shown in the quick example.In v0.24 and earlier, valkeyOperator was accepted inside the deployment spec. Move its install settings to the constructor as shown above. The old shared: true value is accepted and ignored for migration because the webapp always consumes a shared singleton; shared: false is rejected with an actionable error. The runtime field was unsafe because separate KRO instances could request different configurations for the same cluster singleton, so it was removed from the custom-resource schema rather than being silently ignored.
Prerequisites
Install the TypeKro runtime first so Flux and KRO are available. The webapp composition bootstraps CloudNativePG and Valkey operators itself as shared singleton dependencies.
- TypeKro runtime — Flux CD plus KRO controller
- CloudNativePG operator — bootstrapped by this composition
- Valkey operator — bootstrapped by this composition
import { typeKroRuntimeBootstrap } from 'typekro';
// Install runtime first
await typeKroRuntimeBootstrap().factory('direct', { ... }).deploy({ ... });
// Then deploy the full stack; CNPG and Valkey operator singletons are included
await webAppWithProcessing.factory('kro', { ... }).deploy({ ... });Next Steps
- CloudNativePG — PostgreSQL cluster management
- Valkey — Cache cluster management
- Inngest — Workflow engine deployment