Skip to content

What is HOCON?

HOCON (Human-Optimized Config Object Notation) is a superset of JSON designed by Lightbend for configuration files. Every valid JSON document is a valid HOCON document, so adopting it never invalidates what you already have. The specification lives in HOCON.md and is the sole reference these implementations are measured against.

// Both comment styles work
# so do these
app {
name = my-service // '=' or ':' — both are assignment
port = 8080 // no trailing comma required
tags = [a, b, c] // and unquoted strings are ordinary values
}

Keys and string values do not need quotes unless they contain characters that would otherwise be syntax. Quoting is still available and still means exactly what it means in JSON.

A value can reference another value, anywhere in the document, resolved after the whole document is parsed. Forward references work.

host = "localhost"
port = 8080
base-url = "http://"${host}":"${port} // "http://localhost:8080"

${?name} is the optional form. If the path does not resolve, the assignment simply does not happen — which is what makes environment overrides work without a templating layer:

log-level = info
log-level = ${?LOG_LEVEL} // stays "info" when LOG_LEVEL is unset

Objects deep-merge. When the same key appears twice, the later one wins for scalars and merges for objects.

server { host = localhost, port = 8080 }
server { port = 9090 }
// server = { host: "localhost", port: 9090 }

A key may refer to its own previous value, which is how you append to a list without repeating it:

path = [/usr/bin]
path = ${path} [/usr/local/bin]
// += is shorthand for appending one element
extras = [a]
extras += b
include "defaults.conf"
include required(file("secrets.conf"))
include url("https://example.com/shared.conf")

Included documents merge into the point where they appear, so an include at the top provides defaults and one at the bottom overrides.

Durations and byte sizes are part of the language, so the parser hands you a number and not a string to interpret yourself.

timeout = 30s // also: ms, minutes, h, d
retry-delay = 500ms
max-upload = 64MiB // and K, KB, KiB, M, MB, MiB, G, ...
query = """
SELECT *
FROM users
WHERE active = true
"""

Concatenation is real. a = foo bar is the single string "foo bar", not an error. Two adjacent arrays concatenate; two adjacent objects merge. This is what makes "http://"${host} work, and it is also why an accidental space in a value is not always a syntax error.

Whitespace inside unquoted values is preserved between tokens but trimmed at the edges. When in doubt, quote.

Duplicate keys are a feature. No implementation will warn you that you set port twice — setting it twice is how overriding works.

Resolution happens after parsing. A substitution that cannot be resolved is an error at resolve time, not at parse time, and the error names the path that failed rather than a line number in the middle of a merge.

The quick start has the first program in each of the four languages. The compliance page records exactly which parts of the specification each implementation covers, item by item.