Everything Is a Construct
In CDK8s, every resource you define is a Construct—a reusable, composable class that represents one or more Kubernetes resources. Constructs can contain other Constructs, forming a tree. When you call app.synth(), CDK8s walks this tree and renders every Construct into its YAML representation.
This model is borrowed directly from AWS CDK, where the same pattern is used for cloud resources. If you have used AWS CDK, CDK8s will feel immediately familiar.
The Three Levels of Abstraction
CDK8s organizes Constructs into three levels, each offering a different trade-off between control and convenience.
Level 1 (L1): Raw Kubernetes API Objects
L1 Constructs are auto-generated, 1:1 mappings to the Kubernetes API specification. Every field in the Kubernetes API has a corresponding typed property in the L1 class.
- Class naming convention: prefixed with
Kube. For example,KubeDeployment,KubeService,KubeConfigMap. - You must specify every detail manually—selectors, pod templates, container specs—just as you would in raw YAML, but with type safety and autocomplete.
- Use L1 when you need full control or when no higher-level abstraction exists for your resource type.
# L1 example: explicit, verbose, but fully type-safe
k8s.KubeDeployment(self, "my-deployment",
spec=k8s.DeploymentSpec(
replicas=3,
selector=k8s.LabelSelector(match_labels={"app": "api"}),
template=k8s.PodTemplateSpec(...)
)
)
Level 2 (L2): Simplified Standard Library
L2 Constructs are provided by the cdk8s-plus library (a separate package maintained by the CDK8s team). They wrap L1 Constructs with sensible defaults and higher-level methods that reduce boilerplate.
- You do not manually wire labels to selectors—the library handles that.
- Common operations become single method calls:
deployment.expose_via_service(port=80)creates both aServiceand configures the selector automatically. - L2 Constructs enforce Kubernetes best practices by default (e.g., they set
selectorandmatchLabelsconsistently).
# L2 example: concise, opinionated, handles wiring automatically
import cdk8s_plus_27 as kplus
deployment = kplus.Deployment(self, "my-deployment",
containers=[kplus.ContainerProps(image="nginx:1.25")]
)
# Creates a Service and links it to the Deployment automatically
deployment.expose_via_service(port=80)
Level 3 (L3): Company-Specific Patterns
L3 Constructs are custom classes that your team builds on top of L1 or L2. They encode your organization's standards and policies into reusable, versioned components.
- Example: A
CompanyWebServiceConstruct that always adds a liveness probe, sets resource limits, configures a standard logging sidecar, and creates aPodDisruptionBudget. - Developers consuming L3 Constructs only specify what varies (image name, port, replica count). Everything else is enforced by the Construct.
- L3 Constructs are typically published as internal Python packages or npm modules and versioned like application code.
# L3 example: a custom construct your platform team publishes
class CompanyWebService(Construct):
def __init__(self, scope, id, *, image: str, port: int, replicas: int = 2):
super().__init__(scope, id)
deployment = kplus.Deployment(self, "deployment",
replicas=replicas,
containers=[kplus.ContainerProps(
image=image,
liveness=kplus.Probe.from_http_get("/healthz", port=port),
resources=kplus.ContainerResources(
memory=kplus.ResourceQuantity.of("256Mi")
)
)]
)
deployment.expose_via_service(port=port)
A developer using this L3 Construct writes three lines instead of fifty, and the liveness probe and memory limit are guaranteed to be present.
Key Point: The real leverage of CDK8s in a platform engineering context is L3 Constructs. A platform team publishes a versioned library of L3 Constructs. Application teams consume them. When the platform team updates the library (e.g., to add a new required security annotation), all consuming teams get the update by bumping a version number—no YAML search-and-replace across 50 repositories required.