# apple/pkl: a configuration language with types and validation

> Pkl is a configuration as code language from Apple with a JVM-based CLI, editor tooling and bindings for Go, Swift and the JVM. It targets teams whose YAML or properties files have outgrown string substitution.

**apple/pkl** — A configuration as code language with rich validation and tooling.

- Repository: https://github.com/apple/pkl
- Website: https://pkl-lang.org
- Stars: 11,537 · Forks: 405
- Language: Java
- License: Apache-2.0
- Published: 2026-09-21 · Updated: 2026-09-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/apple-pkl

## The problem Pkl targets: configuration that needs a type system

Most configuration formats are data formats that people write by hand. YAML and properties files have no way to say that a port must be an integer between 1 and 65535, that a hostname must match a pattern, or that two fields must agree with each other. Teams work around this by writing validation in application code, by generating config from templates with a string substitution tool, or by reviewing diffs carefully and hoping. Pkl's answer is to make the configuration file a program in a small, statically typed language and to keep the output as plain data.

The intended audience is infrastructure and platform engineers, plus application teams that maintain large configuration trees. The repository layout supports that reading: pkl-k8s templates, a Spring Boot extension (pkl-spring), a Gradle plugin (pkl-gradle), and code generators for Java and Kotlin. The README describes the project in one line as "A configuration as code language with rich validation and tooling," and the surrounding repositories show where the maintainers expect it to be used.

If your configuration is ten lines of key-value pairs, Pkl is more machinery than the problem deserves. The value appears when the same setting is repeated across environments, when a wrong value should fail before deployment rather than at runtime, and when several consumers need the same configuration in different formats.

## How Pkl works: evaluate a typed program, emit plain data

The core mechanism is evaluation. A .pkl file is a module written in the Pkl language; the evaluator runs it and produces a value, which is then rendered in an output format. The README links to a language reference and to a set of examples rather than embedding a specification, so the details of the type system live in the documentation site, not in the repository README.

The repository is organised around that pipeline. pkl-parser and pkl-core hold the language itself. pkl-cli is the command-line front end. pkl-server and pkl-executor exist for embedding evaluation in a host application, which is how the language bindings work: pkl-go, pkl-swift and the JVM libraries call into the evaluator instead of reimplementing it. pkl-formatter and pkl-doc are the tooling layer, and pkl-lsp is a language server, which is what backs the VS Code, IntelliJ, Neovim and TextMate integrations listed in the README.

The design consequence is that configuration becomes a build artefact. You keep .pkl sources in version control, evaluate them in a pipeline, and publish JSON, YAML, XML or property list files for whatever consumes them. That is a different shape from a config format that is read directly at runtime, and it is the main architectural decision to accept or reject.

## Installing the Pkl CLI and evaluating a first file

The README does not contain installation commands. It points to the installation page under pkl-lang.org for the CLI, and to a Sonatype repository for artefacts built by the project's CI pipelines. Use those pages for the current platform packages rather than copying a command from a third-party source, because the CLI ships as platform-specific distributions.

Once the CLI is on your PATH, the first real use is a module plus an output conversion. The command below asks the CLI to evaluate a module and write the result as JSON. The output format is selected by the --format flag, and the file to write is given as the last argument.

## Where Pkl stops being the right tool

The evaluator is the constraint. Pkl is implemented in Java, and the repository is a Gradle multi-project build with a .java-version file at the root, so the CLI and the embedding libraries bring a JVM along. If your configuration is read by a small process on a device where you cannot ship a JVM, or by a shell script that must not depend on a runtime, Pkl is the wrong layer. The language bindings exist precisely because reimplementing the evaluator in another language is not the intended path.

The second limitation is the build step. Because Pkl emits data rather than being read directly, every consumer needs either a generated file or an embedding integration. That is fine in a CI pipeline and awkward in a workflow where an operator edits a file on a production host and restarts a service. Tools that read configuration directly do not have this gap.

The third is maturity of the surrounding surface. The project is not archived and the last push was on 2026-09-21, with 0.32.1 released on 2026-07-23, so the core is moving. But the README lists a large family of repositories (bindings, editor plugins, Kubernetes templates, a Bazel rule set, a package registry), and those pieces are versioned and released separately from the language. A binding that lags the CLI version is a real integration problem, and the README does not describe a compatibility policy across those repositories.

## Pkl compared with Jsonnet and CUE

Jsonnet is the closest comparison and takes a different route to the same goal. It is a templating and data-templating language: you write an object, use functions and imports to compose it, and the output is JSON. Pkl instead defines a language with classes and type annotations, and the evaluator checks those types before producing output. The practical difference is where errors surface. A Jsonnet template that produces a string where a number was expected will usually produce JSON that a consumer rejects later; a Pkl module with a declared type fails during evaluation. Pkl also emits YAML, XML and property lists, not only JSON, which matters if your consumers are not all JSON-based.

CUE occupies a third position. Its selling point is constraint solving over a lattice of values, which lets you merge partial specifications and check that they are consistent. Pkl's model is closer to ordinary object-oriented programming with validation attached to properties. If your problem is merging many overlapping configuration fragments, CUE's unification is the more direct fit. If your problem is that a configuration file should look like a typed program with methods and inheritance, Pkl is the more direct fit. Both approaches require a tool in the pipeline, so that cost is not a differentiator.

## Maintenance, releases and the Apache-2.0 licence

The repository is not archived and the last push was on 2026-09-21. Releases are frequent enough to plan around: 0.32.1 on 2026-07-23, 0.32.0 on 2026-07-08, and 0.31.1 on 2026-03-26. The version numbers are still in the 0.x range, which is the honest signal about upgrade cost. The README links to release notes on the documentation site, and the repository carries a pkl-evolution project for "Suggested Pkl Improvements, Changes, or Enhancements (SPICEs)", which indicates that language changes go through a written proposal process. That is a good sign for anyone tracking breaking changes, but it also means the language is still being shaped.

The licence is Apache-2.0, per the LICENSE.txt file at the repository root, and the repository also carries NOTICE.txt and THIRD-PARTY-NOTICES.txt. Apache-2.0 permits commercial use and modification and includes an explicit patent grant. Two practical points for a review, not legal advice: the NOTICE and THIRD-PARTY-NOTICES files list bundled dependencies, so check them if your organisation has a policy about attribution, and the separate repositories (pkl-go, pkl-swift, pkl-spring, pkl-k8s and the rest) are separate projects whose licences you should read individually rather than assuming they match.

Upgrade cost is dominated by the CLI and the Gradle plugin moving independently. pkl-gradle is its own module in this repository and is published separately, so a Pkl language upgrade and a Gradle plugin upgrade are two decisions. The README does not document a rollback procedure for generated configuration, which means you should treat the generated output as you would any other build artefact and keep the previous version until the new one is verified.

## Conclusion

Adopt Pkl when your configuration has invariants you currently enforce in application code, and when you can accept a JVM-based CLI plus a generated-output step in your pipeline. Do not adopt it if you need a language whose runtime is already present everywhere your config is read, or if you cannot add a build step between the source file and the consumer. Verify first that your target consumers can read one of the output formats (JSON, YAML, XML, property list) and that the Gradle plugin version matches your Gradle release, because pkl-gradle is versioned separately from the CLI.

## FAQ

### What type of file is a .pkl file?

It is a Pkl source module: a program written in the Pkl configuration language, evaluated to produce a value that is then rendered as JSON, YAML, XML or a property list. The README describes Pkl as "A configuration as code language with rich validation and tooling."

### Is Pkl a binary format?

No. Pkl modules are text sources in a typed configuration language, and the evaluator emits text data formats. The repository is a Java codebase with a parser, core evaluator and CLI, not a binary serialization format.

### How do I read a Pkl file in Python?

The repository does not ship Python bindings. The README lists bindings for Go and Swift plus JVM libraries, so the supported path is to evaluate the module with the CLI and consume the emitted JSON, YAML, XML or property list from Python.

### What is apple/pkl?

It is a configuration as code language with rich validation and tooling, maintained under the apple organisation and licensed Apache-2.0. The repository contains the language implementation plus a CLI, formatter, documentation generator, language server and a Gradle plugin.

## Sources

- [apple/pkl on GitHub](https://github.com/apple/pkl)
- [License: Apache-2.0](https://github.com/apple/pkl/blob/main/LICENSE)
- [Project website](https://pkl-lang.org)
- [README](https://github.com/apple/pkl/blob/main/README.md)
- [Releases](https://github.com/apple/pkl/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/apple-pkl
