TL;DR
Pkl (pronounced "Pickle") is Apple's open-source configuration language that catches errors at evaluation time — before your config reaches production. It generates JSON, YAML, and property lists from type-safe, programmable templates.
What Is Pkl?
Pkl solves the "bad config in production" problem:
- Type-safe — catch config errors before deployment
- Programmable — classes, functions, conditionals, imports
- Multi-output — generates JSON, YAML, plist, Java, Kotlin, Swift, Go
- Modular — packages, imports, and inheritance
- IDE support — IntelliJ, VS Code extensions
- Free — Apache 2.0, by Apple
Quick Start
# Install
curl -L https://github.com/apple/pkl/releases/latest/download/pkl-macos-amd64 -o pkl
chmod +x pkl
# Or via Homebrew
brew install pkl
Basic Pkl Configuration
// config.pkl
name = "my-api"
version = "2.1.0"
port = 8080
database {
host = "db.example.com"
port = 5432
name = "myapp"
maxConnections = 20
ssl = true
}
redis {
host = "redis.example.com"
port = 6379
ttl = 3600
}
# Generate JSON
pkl eval config.pkl -f json
# Generate YAML
pkl eval config.pkl -f yaml
Type Safety
// schema.pkl — define your config schema
class DatabaseConfig {
host: String
port: Int(isBetween(1, 65535))
name: String
maxConnections: Int(isPositive)
ssl: Boolean
}
class AppConfig {
name: String(!isEmpty)
version: String(matches(Regex("\\d+\\.\\d+\\.\\d+")))
port: Int(isBetween(1024, 65535))
database: DatabaseConfig
}
// production.pkl — this FAILS at eval time if invalid
amends "schema.pkl"
name = "my-api"
version = "not-a-version" // ERROR: doesn't match semver regex
port = 80 // ERROR: not between 1024-65535
database {
host = "db.prod.com"
port = 99999 // ERROR: not between 1-65535
maxConnections = -5 // ERROR: not positive
}
Environments with Inheritance
// base.pkl
name = "my-api"
replicas = 1
logLevel = "info"
database {
maxConnections = 10
ssl = true
}
// production.pkl
amends "base.pkl"
replicas = 5
logLevel = "warn"
database {
host = "db.prod.internal"
maxConnections = 50
}
// staging.pkl
amends "base.pkl"
replicas = 2
database {
host = "db.staging.internal"
maxConnections = 20
}
Generating Kubernetes YAML
// k8s-deployment.pkl
import "package://pkg.pkl-lang.org/pkl-k8s/k8s@1.0.1#/api/apps/v1/Deployment.pkl"
local config = import("config.pkl")
output {
renderer = new YamlRenderer {}
}
new Deployment {
metadata {
name = config.name
namespace = "default"
}
spec {
replicas = config.replicas
template {
spec {
containers {
new {
name = config.name
image = "myregistry/\(config.name):\(config.version)"
ports {
new { containerPort = config.port }
}
}
}
}
}
}
}
Pkl vs Alternatives
| Feature | Pkl | YAML | JSON | Jsonnet | CUE |
|---|---|---|---|---|---|
| Type safety | Strong | None | None | Weak | Strong |
| Validation | Built-in | External | External | Limited | Built-in |
| Functions | Yes | No | No | Yes | Yes |
| Classes | Yes | No | No | No | Definitions |
| IDE support | Good | Basic | Basic | Fair | Fair |
| Learning curve | Medium | Low | Low | Medium | High |
| Code generation | 8 languages | N/A | N/A | JSON only | JSON/YAML |
Resources
Managing configuration for data pipelines? My Apify scraping tools extract web data with configurable parameters — use Pkl for type-safe scraper configs. Questions? Email spinov001@gmail.com
Top comments (0)