Pkl Cheat Sheet
Overview
Pkl (pronounced “pickle”) is a configuration-as-code language created by Apple and released as open source in 2024. It provides a programmable, scalable, and safe approach to configuration that sits between static formats like JSON/YAML and full programming languages. Pkl combines the simplicity of declarative configuration with the power of a real programming language, including types, validation, functions, conditionals, and code generation for multiple target languages.
Pkl addresses the growing complexity of configuration management by providing schemas for validation, IDE support with autocompletion, and the ability to generate configuration in JSON, YAML, XML, Property Lists, and more. It supports modularity through imports and inheritance, enabling teams to create reusable configuration templates. Pkl code can generate type-safe bindings for Java, Kotlin, Swift, and Go, ensuring configuration values match expected types at compile time.
Installation
Package Managers
# macOS
brew install pkl
# Linux (binary)
curl -L -o pkl https://github.com/apple/pkl/releases/latest/download/pkl-linux-amd64
chmod +x pkl
sudo mv pkl /usr/local/bin/
# macOS Apple Silicon (binary)
curl -L -o pkl https://github.com/apple/pkl/releases/latest/download/pkl-macos-aarch64
chmod +x pkl
sudo mv pkl /usr/local/bin/
# Verify
pkl --version
IDE Support
# VS Code extension
code --install-extension apple.pkl-vscode
# IntelliJ plugin available in JetBrains Marketplace
Core Language
Basic Values
// strings
name = "Pkl"
greeting = "Hello, \(name)!" // String interpolation
multiline = """
This is a
multi-line string
"""
// Numbers
count = 42
price = 19.99
hex = 0xFF
// Boolean
active = true
disabled = false
// Null
optional = null
// Duration
timeout = 30.s
interval = 5.min
ttl = 24.h
// Data size
maxMemory = 512.mb
diskSize = 100.gb
Type System
| Type | Example | Description |
|---|---|---|
String | "hello" | Text value |
Int | 42 | Integer |
Float | 3.14 | Floating point |
Boolean | true | True/false |
Duration | 30.s | Time duration |
DataSize | 512.mb | Data size |
List | List(1, 2, 3) | Ordered collection |
Set | Set(1, 2, 3) | Unique collection |
Map | Map("a", 1) | Key-value pairs |
Listing | Listing block | Ordered elements |
Mapping | Mapping block | Named entries |
Dynamic | Dynamic block | Flexible structure |
Null | null | Absence of value |
Union | String|Int | Either type |
Objects and Classes
// Basic object
server {
host = "localhost"
port = 8080
debug = false
}
// Class definition
class Server {
host: String
port: Int(isBetween(1, 65535))
debug: Boolean = false
protocol: "http"|"https" = "https"
}
// Instantiate
production: Server = new {
host = "api.example.com"
port = 443
protocol = "https"
}
// Class with methods
class Rectangle {
width: Float
height: Float
function area(): Float = width * height
function perimeter(): Float = 2 * (width + height)
}
// Inheritance
class WebServer extends Server {
maxConnections: Int = 1000
corsOrigins: Listing<String>
}
Collections
// Listing (ordered, like arrays)
ports: Listing<Int> = new {
8080
8443
9090
}
// Mapping (key-value, like maps)
labels: Mapping<String, String> = new {
["app"] = "myservice"
["env"] = "production"
["team"] = "platform"
}
// List operations
numbers = List(1, 2, 3, 4, 5)
doubled = numbers.map((n) -> n * 2)
evens = numbers.filter((n) -> n % 2 == 0)
sum = numbers.fold(0, (acc, n) -> acc + n)
Control Flow
// Conditional
environment = "production"
logLevel = if (environment == "production") "warn" else "debug"
// Null coalescing
username = optionalName ?? "anonymous"
// When expression
tier = when {
users < 100 -> "starter"
users < 1000 -> "growth"
users < 10000 -> "business"
else -> "enterprise"
}
// For generator (in listings)
ports: Listing<Int> = new {
for (i in IntSeq(8080, 8085)) {
i
}
}
// Spread operator
base = new Listing { 1; 2; 3 }
extended = new Listing {
...base
4
5
}
Functions and Lambdas
// Local function
local function formatHost(host: String, port: Int): String =
"\(host):\(port)"
serverUrl = formatHost("localhost", 8080)
// Lambda
transform = (x: Int) -> x * 2
result = transform.apply(21) // 42
// Higher-order
services: Listing<String> = new {
"web"
"api"
"worker"
}
urls = services.map((s) -> "https://\(s).example.com")
Validation and Constraints
class DatabaseConfig {
host: String(!isEmpty)
port: Int(isBetween(1, 65535))
name: String(matches(Regex("[a-z][a-z0-9_]*")))
maxConnections: Int(isPositive) = 10
sslMode: "disable"|"require"|"verify-full" = "require"
// Custom validation
connectionTimeout: Duration(this >= 1.s && this <= 60.s) = 10.s
}
class AppConfig {
environment: "development"|"staging"|"production"
debug: Boolean = (environment == "development")
database: DatabaseConfig
// Cross-field validation
hidden replicas: Int(this >= 1) = if (environment == "production") 3 else 1
}
Modules and Imports
// config/base.pkl
module config.base
class ServerConfig {
host: String
port: Int
replicas: Int = 1
}
server: ServerConfig
// config/production.pkl
amends "base.pkl"
server {
host = "api.example.com"
port = 443
replicas = 3
}
// config/main.pkl
import "base.pkl"
import "package://pkg.pkl-lang.org/pkl-pantry/pkl.toml@1.0.0#/toml.pkl"
// Use imported module
myServer: base.ServerConfig = new {
host = "localhost"
port = 8080
}
Output Formats
CLI Commands
| Command | Description |
|---|---|
pkl eval config.pkl | Evaluate and print Pkl output |
pkl eval -f json config.pkl | Output as JSON |
pkl eval -f yaml config.pkl | Output as YAML |
pkl eval -f xml config.pkl | Output as XML |
pkl eval -f plist config.pkl | Output as Property List |
pkl eval -f properties config.pkl | Output as Java Properties |
pkl eval -o output.json config.pkl | Write to file |
pkl eval -m . config.pkl | Multi-file output |
pkl test tests/ | Run Pkl tests |
pkl project package | Create distributable package |
Generating JSON
# Generate JSON
pkl eval -f json config.pkl
// config.pkl
server {
host = "localhost"
port = 8080
features = new Listing {
"auth"
"logging"
"metrics"
}
}
Output:
{
"server": {
"host": "localhost",
"port": 8080,
"features": ["auth", "logging", "metrics"]
}
}
Generating YAML
pkl eval -f yaml config.pkl
Generating Multiple Files
// k8s.pkl
output {
files {
["deployment.yaml"] = new YamlRenderer { value = deployment }
["service.yaml"] = new YamlRenderer { value = service }
["configmap.yaml"] = new YamlRenderer { value = configMap }
}
}
Configuration
Project File (PklProject)
// PklProject
amends "pkl:Project"
package {
name = "myconfig"
version = "1.0.0"
baseUri = "package://example.com/myconfig"
packageZipUrl = "https://example.com/myconfig/\(version)/myconfig@\(version).zip"
}
dependencies {
["pkl-pantry"] = import("package://pkg.pkl-lang.org/pkl-pantry/pkl.toml@1.0.0")
}
Code Generation
# Generate Java classes
pkl-codegen-java config.pkl -o src/main/java
# Generate Kotlin classes
pkl-codegen-kotlin config.pkl -o src/main/kotlin
# Generate Swift structs
pkl-codegen-swift config.pkl -o Sources/Config
# Generate Go structs
pkl-gen-go config.pkl -o internal/config
Advanced Usage
Kubernetes Configuration
// k8s/deployment.pkl
import "package://pkg.pkl-lang.org/pkl-pantry/k8s@1.0.0#/api/apps/v1/Deployment.pkl"
deployment: Deployment = new {
metadata {
name = "myapp"
namespace = "production"
labels {
["app"] = "myapp"
["version"] = "1.0.0"
}
}
spec {
replicas = 3
selector {
matchLabels {
["app"] = "myapp"
}
}
template {
metadata {
labels {
["app"] = "myapp"
}
}
spec {
containers {
new {
name = "myapp"
image = "myapp:1.0.0"
ports {
new { containerPort = 8080 }
}
resources {
requests {
["cpu"] = "100m"
["memory"] = "128Mi"
}
limits {
["cpu"] = "500m"
["memory"] = "512Mi"
}
}
}
}
}
}
}
}
Testing
// tests/config_test.pkl
amends "pkl:test"
import "../config.pkl"
facts {
["server port is valid"] {
config.server.port > 0
config.server.port < 65536
}
["production has replicas"] {
config.server.replicas >= 3
}
}
examples {
["default config"] {
new config.ServerConfig {
host = "localhost"
port = 8080
}
}
}
pkl test tests/
Template Patterns
// templates/service.pkl
abstract class MicroService {
name: String
port: Int
replicas: Int = 1
environment: Mapping<String, String> = new {}
// Computed properties
hidden fullName: String = "svc-\(name)"
hidden url: String = "http://\(fullName):\(port)"
}
// services.pkl
import "templates/service.pkl"
api = new MicroService {
name = "api"
port = 8080
replicas = 3
environment {
["LOG_LEVEL"] = "info"
["DB_HOST"] = "postgres"
}
}
worker = new MicroService {
name = "worker"
port = 9090
environment {
["QUEUE_URL"] = "redis://redis:6379"
}
}
Troubleshooting
| Problem | Solution |
|---|---|
| Type validation error | Check constraints on class properties; use pkl eval to see errors |
| Import not found | Check module URI; run pkl project resolve for packages |
| Circular dependency | Restructure modules; use amends for inheritance chains |
| Output format wrong | Specify format with -f json, -f yaml, etc. |
| IDE not showing errors | Install Pkl VS Code extension; check language server is running |
| Code generation fails | Ensure classes have explicit types; check codegen plugin version |
| Memory issues with large configs | Split into smaller modules; use lazy evaluation |
| Package resolution fails | Run pkl project resolve; check PklProject dependencies |