hashicorp/hcl: a Go toolkit for building configuration languages
HCL is the HashiCorp configuration language.
At a glance
- What is it?
- HCL is not a config file format you adopt off the shelf. It is a parser, decoder and writer library that a Go application uses to define its own configuration language, with a native syntax and a JSON equivalent. Here is what the repository documents, how to get a first program running, and where the approach stops paying off.
- Who is it for?
- Adopt hashicorp/hcl if you are writing a Go tool that needs a user-facing configuration language with expressions, source positions in error messages, and a machine-generatable JSON form. Do not adopt it if you only need to read a fixed file format, or if your project is not on Go Modules, since HCL 2 cannot be imported from a non-modules project.
- Can I use it commercially?
- Yes, with conditions. MPL-2.0 is a weak copyleft licence: you can use it inside commercial and closed-source software, but if you distribute changes to its own files, you must publish those changes under the same licence.
- Is it still maintained?
- Yes. The repository last received commits 8 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What hashicorp/hcl actually is, and who writes code against it
The README opens by calling HCL a toolkit for creating structured configuration languages that are both human- and machine-friendly, for use with command-line tools. That word toolkit is the whole story. HCL is not a file format with a fixed set of keys. The calling application defines which attribute names and nested block types are expected, and HCL parses the configuration file, verifies that it conforms to the expected structure, and returns high-level objects the application can process further. The audience is therefore narrow and specific: developers building devops tools, servers and command-line programs that need a configuration surface. If you are a user looking for a config format to write by hand, you are not the target reader of this library; you are the user of some tool that embeds it. Terraform is the best known such tool, and the README says HCL and its predecessor HIL originate in HashiCorp Terraform. The repository also ships a cmd/ directory and specsuite/, which suggests the project maintains its own specification test material alongside the library, though the README does not describe those directories.
Attributes, blocks, and why the application owns the schema
HCL is built around two primary concepts. Attributes are key-value pairs. Blocks are named, labelled groups that can nest. The README's hypothetical example has an io_mode attribute and a service block labelled http and web_proxy, containing a listen_addr attribute and two nested process blocks. The JSON equivalent nests objects in the same shape, with the labels becoming keys. The important consequence is that the syntax is shared but the meaning is not. HCL parses the file, checks it against the structure the application declared, and hands back values. That is why the README argues HCL is not competing with JSON or YAML on serialization: those are formats for serializing data structures, while HCL is a syntax and API for building structured configuration formats. The payoff the README claims is better error messages and more convenient definition inside the calling application. The trade-off is equally clear: without an application schema, an HCL file is not valid or invalid in any meaningful sense. Nothing in the repository defines a universal HCL document.
Expressions, interpolation and the two syntaxes
Attribute values can be expressions rather than literals. The README shows arithmetic against an application-provided variable, string interpolation with ${name}, and calls to application-provided functions such as upper(message). HCL 2.0 is where this came from: the release combined HCL 1.0 with the interpolation language HIL into a single language supporting arbitrary expressions. JSON cannot carry expressions directly, so the JSON representation allows arbitrary expressions inside strings using the same ${...} form. That gives a real workflow: humans author native HCL, machines emit JSON, and the calling application decodes both through the same API. The README states the API within the calling application is the same regardless of which syntax is used. Note the phrase with support from the calling application. Variables and functions are not built into HCL; the application supplies them. A reader expecting a self-contained expression language with a standard library will not find one here.
Installing HCL and decoding a first file with hclsimple
HCL is a Go module at github.com/hashicorp/hcl/v2, so installation is a module fetch rather than a system package. The go.mod in the repository declares go 1.25.0 and pulls in github.com/zclconf/go-cty for values, github.com/agext/levenshtein for the did-you-mean suggestions, and text segmentation libraries for Unicode handling. Add the module to a Go Modules project with:
go get github.com/hashicorp/hcl/v2The README's own example is the shortest path to a working program. It defines Go structs whose tags describe the expected configuration, then decodes a file into them with hclsimple.DecodeFile. The tags carry the meaning: hcl:"io_mode" maps an attribute, hcl:"service,block" declares a block, and hcl:"protocol,label" marks a block label. Copy it into a main package:
package main
import (
"log"
"github.com/hashicorp/hcl/v2/hclsimple"
)
type Config struct {
IOMode string `hcl:"io_mode"`
Service ServiceConfig `hcl:"service,block"`
}
type ServiceConfig struct {
Protocol string `hcl:"protocol,label"`
Type string `hcl:"type,label"`
ListenAddr string `hcl:"listen_addr"`
Processes []ProcessConfig `hcl:"process,block"`
}With config.hcl written in the native syntax from the README, the call is a single line. The second argument is nil, which the README passes for the evaluation context; supplying variables or functions means passing a non-nil context instead:
err := hclsimple.DecodeFile("config.hcl", nil, &config)
if err != nil {
log.Fatalf("Failed to load configuration: %s", err)
}If decoding succeeds, the program logs the populated Config struct. If it fails, the error carries the diagnostic information HCL is designed to produce, which is the main reason to use the library rather than a generic unmarshaller.
When hclsimple is too small, and what the lower-level packages cost you
hclsimple is a convenience layer. The README is explicit that a lower-level API is available for applications that need more control over parsing, decoding and evaluation, and points to the package documentation. The repository layout shows what that means in practice: hclsyntax and json hold the two parsers, hclparse handles file loading, hcldec does schema-driven decoding, gohcl maps to Go structs, and hclwrite generates HCL source programmatically. That is a lot of surface area, and choosing among them is a design decision you cannot defer. The repository also exposes traversal.go and structure_at_pos.go, which exist to answer questions like what expression refers to and what block sits at a given byte offset. Those are the building blocks for editor integration and precise diagnostics. The cost is that you are now maintaining a schema layer, a value model and an evaluation context rather than a struct and a function call. For a tool with a handful of settings, that is over-engineering. For a tool whose configuration is the product, it is the reason to pick HCL.
The version split is the sharpest edge in the repository
HCL 2 has a completely new parser and Go API with no direct migration path from HCL 1. The README says the syntax is similar but the implementation takes very different approaches. More restrictive than the API change is the import rule: HCL 2 cannot be imported from Go projects that are not using Go Modules. The README directs readers to a version selection guide on the wiki for the details. In practice this means the module path is github.com/hashicorp/hcl/v2, and a program that needs both generations must alias them, which the README demonstrates with hcl1 and hcl2 import names. If you maintain a Go tool built before modules, or vendored without a go.mod, HCL 2 is simply unavailable until that changes. This is the clearest case where HCL is the wrong tool: not because of the language design, but because of the packaging history.
Alternatives, and the difference that matters
The obvious alternative for a Go program is encoding/json from the standard library. The difference is not syntax preference. With encoding/json you get a decoder and no schema negotiation: unknown keys are ignored unless you opt into strict decoding, and errors describe JSON structure rather than the meaning of your configuration. HCL's model is the reverse, since the application declares expected attribute names and block types first and the parser checks conformance against that declaration, which is what the README means by better error messages. The other alternative is embedding a general-purpose scripting language, which the README positions HCL against directly: HCL attempts to strike a compromise between generic serialization formats such as JSON and configuration formats built around full programming languages such as Ruby. If your users genuinely need loops, imports and arbitrary computation, a real language will serve them better than HCL expressions plus application-provided functions. If they need to write down settings that a tool validates and explains, HCL's declarative model is the smaller commitment. The JSON variant also gives you an escape hatch: machine-generated configuration can be JSON while humans keep the native syntax, decoded by the same application code.
Maintenance, licence and the cost of upgrading
The repository is not archived, and the last push was on 2026-09-22. Release cadence is worth reading carefully before pinning a version: v2.25.0 was tagged on 2026-09-15, v2.24.0 on 2025-07-07, and v2.23.0 on 2024-11-15. That is roughly one release per year with a gap of more than a year between v2.23.0 and v2.24.0, so a team that pins to a specific tag should expect to sit on it for a long time. The project is licensed under MPL-2.0, a file-level copyleft licence. Modifying HCL's own source files carries obligations that importing the module does not, but the boundary between the two is a legal question this article cannot answer. The Makefile shows the project's own quality gates: fmtcheck runs scripts/gofmtcheck.sh, vetcheck runs go vet ./..., and copyrightcheck runs the copywrite tool in plan mode, with check combining all three. There is no documented migration guide between minor versions in the repository, so the practical upgrade cost is reading the CHANGELOG and re-running your own configuration fixtures. The specsuite/ directory and the three spec documents the README links (spec.md, hclsyntax/spec.md, json/spec.md) are the reference to test against when behaviour shifts.
Editorial conclusion
Adopt hashicorp/hcl if you are writing a Go tool that needs a user-facing configuration language with expressions, source positions in error messages, and a machine-generatable JSON form. Do not adopt it if you only need to read a fixed file format, or if your project is not on Go Modules, since HCL 2 cannot be imported from a non-modules project. Before committing, verify three things in your own tree: that hclsimple.DecodeFile accepts your struct tags, that the JSON form of your configuration round-trips through the same decoder, and that the hclsimple API is rich enough for your error reporting, because the README points to the lower-level packages when it is not.
Frequently asked questions
What is HCL in HashiCorp?
HCL is the HashiCorp configuration language, distributed as a Go library that applications use to build their own structured configuration formats. It provides a native syntax and a JSON variant, and the calling application defines which attributes and blocks are expected.
Is HCL the same as Terraform?
No. HCL is the configuration language toolkit, and the README states that HCL and HIL originate in HashiCorp Terraform. Terraform is an application that embeds HCL; the library itself defines no application-specific configuration.
What is an HCL file in Terraform?
It is a configuration file written in the native HCL syntax, made of attributes and labelled blocks. The README shows the same configuration expressed in native syntax and as an equivalent JSON object, both decodable by the same application API.
What is HCL in GitHub?
The GitHub repository hashicorp/hcl is the home of the HCL library, where the Go module github.com/hashicorp/hcl/v2, the native syntax and JSON specifications, and the decoder packages such as hclsimple, gohcl and hcldec live.
What is hashicorp hcl?
It is the same project: a toolkit for creating structured configuration languages, with a native syntax inspired by libucl and nginx configuration, plus a JSON-based variant that is easier for machines to generate and parse.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/hashicorp-hcl)