Custom Config
Every Kyku resource accepts an optional customConfig field: a plain object of provider-native values that get merged directly into the underlying cloud API request. It’s the escape hatch for the long tail of provider-specific options Kyku’s portable resource shape doesn’t (and won’t) model as first-class fields.
import { Vpc } from '@kykucloud/types'import type { AwsVpcCustomConfig } from '@kykucloud/aws'
const vpc = new Vpc({ id: 'main-vpc', name: 'main', cidr: '10.0.0.0/16', region: 'us-east', customConfig: { InstanceTenancy: 'dedicated', } satisfies AwsVpcCustomConfig,})Portable fields vs. customConfig
Section titled “Portable fields vs. customConfig”A resource like Vpc or Vm has a small set of portable, first-class fields — name, cidr, instanceType, image, network, and so on — that work identically across every provider. customConfig is the opposite: provider-native fields, shaped exactly like that provider’s own create/update request, that only make sense for the provider you’re deploying to.
Kyku merges customConfig directly into the request it sends to the cloud API. It doesn’t reinterpret or rename anything: values use the provider’s own field names and casing (CidrBlock for AWS, ip_range for Hetzner/DigitalOcean, ipCidrRange for GCP), because the whole point is to reach fields Kyku’s abstraction doesn’t cover.
Typed aliases
Section titled “Typed aliases”Each provider package exports typed aliases so you get autocomplete and a type error instead of a typo silently doing nothing:
| Provider | Source | Example |
|---|---|---|
| AWS | @aws-sdk/client-* command input types |
AwsVpcCustomConfig, AwsVmCustomConfig (from @kykucloud/aws) |
| GCP | @google-cloud/compute request/resource types |
GcpVpcCustomConfig, GcpVmCustomConfig (from @kykucloud/gcp) |
| Hetzner | Generated from the official cloud.spec.json OpenAPI document |
HetznerVpcCustomConfig, HetznerVmCustomConfig (from @kykucloud/hetzner) |
| DigitalOcean | Generated from the official digitalocean/openapi spec |
DigitaloceanVpcCustomConfig, DigitaloceanVmCustomConfig (from @kykucloud/digitalocean) |
Each alias is the provider’s real request type minus the fields Kyku’s portable contract already owns (name, cidr, instanceType, network/security-group identity, tags, SSH keys, and so on) — so it grows and shrinks with the underlying SDK/API automatically, with no hand-maintained field list to go stale.
Apply an alias with satisfies rather than a type annotation, so the object literal itself is still checked structurally:
customConfig: { EbsOptimized: true } satisfies AwsVmCustomConfigCoverage is broadest for AWS (every resource manager has an alias) and GCP (every Compute-backed resource plus most of the rest, via SDK dependencies added purely for their request types — Identity/Role are the one documented gap, since no clean IAM Admin client library exists). Hetzner and DigitalOcean cover their most commonly customized resources (Vpc, Vm, SecurityGroup, LoadBalancer, plus SshKey for Hetzner and Database/DnsRecord/KubernetesCluster for DigitalOcean); a handful of resources on each (object storage, legacy DNS APIs, synthetic tag-based resources) don’t have — and structurally can’t usefully have — a generated alias, which is documented per-resource rather than silently missing. Untyped doesn’t mean unsupported: the merge/read/update passthrough exists for every resource on every provider regardless of whether a typed alias exists. Run kyku schema <resourceType> --provider <provider> to check current coverage for a specific pair rather than relying on this description staying up to date.
Unknown keys
Section titled “Unknown keys”A key with no typed alias, or a key a generated alias doesn’t yet know the lifecycle of, is still accepted — Kyku warns and passes it through rather than hard-rejecting it, so a brand-new field the cloud API just shipped isn’t blocked by a stale catalog. If the key is a near-miss for a known one (a likely typo), the warning suggests the closest match (“did you mean ‘description’?”). Run kyku validate to see these warnings — they’re informational, not errors, so they don’t fail validation. The one thing that is a hard error is a customConfig key that collides with a portable field Kyku already manages (for example, setting AWS’s CidrBlock when you also set cidr — Kyku doesn’t know which one should win, so it refuses to guess).
Create-only vs. updatable keys
Section titled “Create-only vs. updatable keys”Most customConfig keys are create-only: change one after the resource exists, and the next kyku plan shows a replace (destroy + recreate), because most cloud request fields aren’t safely re-postable in place. A small, explicitly curated set of keys per provider/resource is marked lifecycle-aware and plans an in-place update instead once the provider manager implements the corresponding update/action API call:
- Hetzner Vpc:
expose_routes_to_vswitch - DigitalOcean Vpc:
description,default - GCP Vpc:
description,mtu,routingConfig
Everything else defaults to create-only until a manager grows the matching update path. Kubernetes is the one exception to the “unlisted = create-only” default: every typed Kubernetes resource applies via server-side apply (an idempotent PATCH), so there’s no field that genuinely requires tearing the object down — customConfig changes on Kubernetes resources always plan as an update.
Run kyku schema <resourceType> --provider <provider> to see exactly which keys are lifecycle-aware for a given resource/provider pair right now, rather than relying on this list staying current — see the schema CLI reference.
Irreversible and one-way changes
Section titled “Irreversible and one-way changes”A lifecycle-aware key means Kyku can apply the change in place — it doesn’t mean the underlying cloud operation is itself reversible. A few routed keys are one-way on the provider’s side; Kyku lets you apply them but doesn’t pretend the cloud will let you undo them:
- DigitalOcean Vm
ipv6: DigitalOcean supports enabling IPv6 on a running Droplet but not disabling it again. Settingipv6: falseafter it’s alreadytruethrows rather than silently no-op’ing or attempting an API call DigitalOcean would reject anyway. - DigitalOcean Vm
resize.disk: growing the disk via a resize action is permanent — a Droplet’s disk can never be shrunk again afterward. The resize also requires the Droplet to be powered off first; Kyku does not power-cycle it for you, so DigitalOcean rejects the action if the Droplet is running when the plan applies. - AWS Vpc
InstanceTenancy: changing'dedicated'to'default'only affects instances launched after the change — existing dedicated instances stay dedicated. Left create-only rather than modeled as a true in-place toggle, since “in place” would be misleading here.
Stopped-instance preconditions
Section titled “Stopped-instance preconditions”Some EC2 instance attributes can only change while the instance is stopped (InstanceType,
enhanced-networking flags, kernel/ramdisk, and similar). Kyku’s AWS Vm routing deliberately
does not implement an automatic stop → modify → restart cycle for these — doing so would
mean silently taking a running instance offline mid-plan, which is a materially different (and
riskier) operation than every other lifecycle-aware key documented here, all of which apply
without any downtime. Attributes in this category are left create-only (a replace, not an
update) rather than wrapped in an implicit reboot; if you need one of them changed in place,
stop the instance yourself first. Only attributes AWS supports changing on a running instance
are routed to update() — see packages/aws/src/resources/vm.ts for the current list.
Kubernetes
Section titled “Kubernetes”Kubernetes resources accept customConfig too. It merges into the same target the pre-existing raw spec field already used on K8sDeployment/K8sStatefulSet/K8sIngress — applied after spec, so customConfig wins on any overlapping key. Resources without a spec of their own (K8sConfigMap, K8sSecret) merge customConfig into the whole object instead, with identity fields and anything already owned by a first-class field (data, stringData, secretType, …) protected from being overwritten.
K8sManifest and K8sHelmRelease don’t accept customConfig — the raw manifest or Helm values you hand them are already the full provider-native escape hatch, so there’s no separate merge target that would mean anything.
Secrets
Section titled “Secrets”customConfig values are stored in Kyku’s state file like any other config — including in plaintext if you put a secret directly in it. Use an Kyku Secret reference (or your provider’s native secret-reference mechanism) for anything sensitive rather than inlining it into customConfig.