Learn
Authoring Templates
How to design reusable Pkl templates, environment overlays, and extension points.
Authoring Templates
Templates are the point where Pkl starts to feel different from plain data. Instead of copying a complete configuration per environment, write a base module that declares the shape and defaults, then use smaller modules to amend it.
Template Contract
A good template tells readers three things:
-
which members are required
-
which members have safe defaults
-
which members are intended extension points
// service.pklclass Service { name: String port: Int(isBetween(1, 65535)) = 8080 replicas: Int(isPositive) = 1}service: Service = new Service { name = "api"}The class is the contract. The service value is the default instance. A
specialized module can amend the value without restating the whole object.
Environment Overlays
Use amends when the child module is still the same configuration with a
smaller set of differences.
// prod.pklamends "service.pkl"service { replicas = 4}The implementation merges module members from the parent, then applies the child's overrides. This is the same mental model as object amendment, lifted to the module boundary.
Library Modules
Use imports when a module is a library rather than a parent template.
// ports.pklapi = 8080admin = 9090import "ports.pkl" as portsservice { port = ports.api}This keeps reusable constants or class declarations separate from the module that owns the rendered output.
Extension Points
Prefer explicit nested objects for areas users are expected to change.
service { name = "api" runtime { image = "example/api" args = new Listing {} }}Then the child module has a stable target:
amends "service.pkl"service { runtime { args = new Listing { "--log-level=debug" } }}Avoid Template Drift
Avoid copying the entire base module into every environment. Once values are copied, the base template stops being useful and readers must diff files by hand.
Instead:
-
put required shape in a class
-
put shared defaults in the base module
-
keep environment files small
-
use type aliases for repeated constraints
-
keep host-tool-specific output concerns out of the template when possible
Template Coverage Notes
The core template path is module amends, module extends, object amendments,
typed object expressions, class property defaults, and constraints. Keep
templates readable before reaching for broad standard-library helpers,
generators, or format-specific output behavior.