Nickel is the universal configuration language. Nickel is configuration templating, but done right. Modular, correct and concise.
Its purpose is to automate the generation of static configuration files - think JSON, YAML, XML, or your favorite data representation language - that are then fed to another system. It is designed to have a simple, well-understood core: it is in essence JSON with functions.
Nickel's salient traits are:
-
Lightweight: Nickel is easy to embed. An interpreter should be simple to implement. The reference interpreter can be called from many programming languages.
-
Composable code: the basic building blocks for computations are functions. They are first-class citizens, which can be passed around, called and composed.
-
Composable data: the basic building blocks for data are records (JSON's objects). Records can be composed through the merge operator, combining metadata as well (documentation, default values, type contracts, etc).
-
Typed, but only when it helps: one one hand, static types improve code quality, serve as documentation and eliminate bugs early, in particular for functions.
On the other hand, most configuration data are static. In this case, dynamic type errors are often simple and sufficient. Additionally, some JSON schemas are hard to translate to more rigid types.
Nickel features a (sound) gradual type system: it has types, but you get to choose when you want to use them or not. You can statically check complex functions, but use more flexible runtime validation for configuration data.
-
Design by contract: contracts are a principled approach to validation. In a Nickel configuration, contracts act like schemas. You can write your own, mix them with existing contracts, and enforce that your configuration is correct by sprinkling simple type-like annotations.
The motto guiding Nickel's design is:
Great defaults, design for extensibility
There should be one clear and simple path for common tasks. But the day you need to go beyond, there should be no arbitrary restrictions that limit what you can do.
Nickel is designed, developed, and maintained by Tweag, a part of Modus Create. We develop it in the open, and gratefully accept community feedback and contributions.
Nickel is a good fit in any situation where you need to generate a complex configuration, be it for a single app, a machine, whole infrastructure, or a build system.
The motivating use cases are in particular:
- The Nix package manager: Nix is a declarative package manager using its own language for specifying packages. Nickel is an evolution of the Nix language, while trying to overcome some of its limitations.
- Infrastructure as code: infrastructure is becoming increasingly complex, requiring a rigorous approach to deployment, modification and configuration. This is where a declarative approach also shines, as adopted by Terraform, NixOps or Kubernetes, all requiring potentially complex generation of configuration.
- Build systems: build systems (like Bazel) need a specification of the dependency graph.
Most aforementioned projects have their own bespoke configuration language. See Comparison. In general, application-specific languages might suffer from feature creep, lack of abstractions or just feel ad hoc. Nickel buys you more for less.
Related projects that are part of the Nickel ecosystem:
- nickel-kubernetes: a collection of auto-generated Nickel contracts (schemas) for Kubernetes resources.
- Terraform-Nickel: write Terraform configuration with Nickel
- Organist: batteries included environments with Nickel inside
- json-schema-to-nickel: generate Nickel contracts from JSON schema specifications.
- rules_nickel: generate configuration files using Nickel during a Bazel build
- The nickel-lang organization hosts various smaller projects, including a tree-sitter grammar definition for Nickel and editor plugins.
Please follow the getting started guide for Nickel users on the nickel-lang
website. The instructions below are
either reproduced for this document to be self-contained or because
they are aimed toward hacking on the Nickel interpreter itself (e.g. building
the nickel-lang-core crate documentation).
-
Get a Nickel binary:
- With flake-enabled Nix, run
Nickel directly with
nix run nixpkgs#nickel. You can use our binary cache to prevent rebuilding a lot of packages. Pass arguments to Nickel with an extra--as innix run nixpkgs#nickel -- repl. Usegithub:tweag/nickelto run the unstable version (masterin practice). - Again with flake-enabled Nix, you can install Nickel in your profile with
nix profile install nixpkgs#nickel. Thenickelcommand is then in your$PATHand is available anywhere. - If you're running macOS you can use Homebrew to install the Nickel binary
with
brew install nickel. - Without Nix, you can use
cargo run --bin nickelafter building, passing arguments with an extra--as incargo run --bin nickel -- eval program.ncl.
- With flake-enabled Nix, run
Nickel directly with
-
Run your first program:
$ nickel eval <<< '["hello", "world"] |> std.string.join ", "' "hello, world"
Or load it from a file:
$ echo 'let s = "world" in "hello, %{s}"' > program.ncl $ nickel eval program.ncl "hello, world"
-
Start a REPL:
$ nickel repl nickel> {"hello" = true, "world" = true, "universe" = false} |> std.record.to_array |> std.array.filter (fun {field, value} => value) |> std.array.map (fun {field, value} => field) |> std.string.join ", " "hello, world"
Use
:helpfor a list of available commands. -
Export your configuration to JSON, YAML or TOML:
$ nickel export --format json <<< '{content = "hello, world"}'
{
"content": "hello, world"
}Use nickel help for a list of subcommands, and nickel help <subcommand>
for help about a specific subcommand.
To get in touch, you can join our
server.
Nickel has syntax highlighting plugins for Vim/Neovim, Emacs and VSCode. It also has a tree-sitter grammar which provides highlighting in an editor-independent way. The Nickel Language Server (NLS) provides in-editor diagnostics, type hints, and auto-completion. Please follow the LSP guide to set up syntax highlighting and NLS.
To format one or several Nickel source files, use nickel format:
nickel format network.ncl container.ncl api.nclNickel uses Topiary to format Nickel code under the hood. The Nickel Language Server also provides formatting capabilities out of the box.
-
Download build dependencies:
-
With Nix: If you have Nix installed:
nix-shell # if you don't use Nix flakes nix develop # if you use Nix flakes
You will be dropped in a shell, ready to build. You can use our binary cache to prevent rebuilding a lot of packages.
-
Without Nix: otherwise, follow this guide to install Rust and Cargo first.
-
-
Build Nickel:
cargo build -p nickel-lang-cli --releaseAnd voilà! Generated files are placed in
target/release.You can directly build and run the Nickel binary and pass argument after
--by usingcargo run:cargo run --bin nickel --release -- eval foo.ncl
Run tests with
cargo testThe user manual is available on the nickel-lang.org
website, and in this
repository as a collection of Markdown files in doc/manual.
To get the documentation of the nickel-lang codebase itself:
-
Build the doc:
cargo doc --no-deps -
Open the file
target/doc/nickel/index.htmlin your browser.
You can find examples in the ./examples directory.
Since version 1.0 released in May 2023, the core design of the language is stable and Nickel is useful for real-world applications. The next steps we plan to work on are:
The next steps we plan to work on are:
- Incremental evaluation: design an incremental evaluation model and a caching mechanism in order to perform fast re-evaluation upon small changes to a configuration.
- Implement a bytecode compiler and virtual machine (RFC007) for improved performance
- Custom merge functions (second part of the overriding proposal)
- CUE is a configuration language with a focus on data validation. It introduces a new constraint system backed by a solid theory which ensures strong guarantees about your code. It allows for very elegant schema specifications. In return, the cost to pay is to abandon functions and Turing-completeness. Nickel's merge system is inspired by the one of CUE, even if since Nickel does have general functions and is Turing-complete, they are necessarily different.
- Nix: The Nix language, or Nix expressions, is one of the main inspirations for Nickel. It is a very simple yet powerful lazy functional language. We strive to retain this simplicity, while adding typing capabilities, modularity, and detaching the language from the Nix package manager.
- Dhall is a statically typed configuration language. It is also inspired by Nix, to which it adds a powerful static type system. However, this forces the programmer to annotate all of their code with types.
- Jsonnet is another language which could be dubbed as "JSON with functions" (and others things as well). It is a lazy functional language with object-oriented features, among which inheritance is similar to Nickel's merge system. One big difference with Nickel is the absence of typing.
- KCL is a gradually typed configuration language whose validation is based on object-oriented schemas that can be extended through inheritance. Unlike the languages above, its evaluation is strict.
- Pulumi is not a language in itself, but a cloud tool (like Terraform) where you can use your preferred language for describing your infrastructure. This is a different approach to the problem, with different trade-offs.
- Starlark is the language of Bazel, which is a dialect of Python. It does not have types and recursion is forbidden, making it not Turing-complete.
See RATIONALE.md for the design rationale and a more detailed comparison with these languages.
| Language | Typing | Recursion | Evaluation | Side-effects |
|---|---|---|---|---|
| Nickel | Gradual (dynamic + static) | Yes | Lazy | Yes (constrained, planned) |
| Starlark | Dynamic | No | Strict | No |
| Nix | Dynamic | Yes | Lazy | Predefined and specialized to package management |
| Dhall | Static (requires annotations) | Restricted | Lazy | No |
| CUE | Static (everything is a type) | No | Lazy | No, but allowed in the separated scripting layer |
| Jsonnet | Dynamic | Yes | Lazy | No |
| KCL | Gradual (dynamic + static) | Yes | Strict | No |
| JSON | None | No | Strict | No |
| YAML | None | No | N/A | No |
| TOML | None | No | N/A | No |