Writing CCL
This guide teaches you how to write CCL configuration files from scratch. By the end, you’ll be comfortable expressing any configuration structure in CCL.
Your First CCL File
Section titled “Your First CCL File”A CCL file is a list of key-value pairs separated by =:
name = My Applicationversion = 1.0.0That’s valid CCL. Every value is a string --- there are no special types, no quoting rules, and no escaping needed.
Leading and trailing whitespace is trimmed from both keys and values, so name = My Application is equivalent to name = My Application.
Adding Structure with Indentation
Section titled “Adding Structure with Indentation”To group related settings, give a key an empty value and indent the children:
server = host = 0.0.0.0 port = 8080
database = host = localhost port = 5432You can nest as deeply as you need:
logging = outputs = console = enabled = true format = json file = enabled = true path = /var/log/app.logWriting Lists
Section titled “Writing Lists”Use empty keys (= value) to create list items:
allowed_origins = = https://example.com = https://api.example.com = https://admin.example.comThis produces the list ["https://example.com", "https://api.example.com", "https://admin.example.com"].
You can also create lists by repeating the same key at the top level:
server = web1.example.comserver = web2.example.comserver = web3.example.comAdding Comments
Section titled “Adding Comments”Comments use the /= syntax:
/= Database configuration/= Last updated: 2025-02-25
database = host = localhost port = 5432 /= Credentials are loaded from environment variables username = ${DB_USER} password = ${DB_PASS}Comments must be on their own line. There are no inline comments --- everything after the first = on a line is the value:
port = 8080 /= This is NOT a commentIn this example, the value of port is the string "8080 /= This is NOT a comment".
Writing Multiline Values
Section titled “Writing Multiline Values”Indent continuation lines beneath the key to create multiline values:
description = This application handles user authentication and session management for the main web portal.The value preserves the newlines and indentation of the continuation lines.
Special Characters
Section titled “Special Characters”Equals Signs in Values
Section titled “Equals Signs in Values”Only the first = on a line separates key from value. Additional = characters are part of the value:
url = https://example.com?key=value&token=abc123equation = E = mc^2env_override = NODE_ENV=productionDots in Keys
Section titled “Dots in Keys”Dots are literal characters in keys, not path separators:
server.host = localhostapp.config.version = 2These create keys literally named "server.host" and "app.config.version". For hierarchical structure, use indentation instead:
server = host = localhost
app = config = version = 2Unicode
Section titled “Unicode”CCL fully supports Unicode in both keys and values:
greeting = Hello, 世界! 🌍author = François Müllerdescription = Ñoño configurationPatterns and Recipes
Section titled “Patterns and Recipes”Environment-Specific Configuration
Section titled “Environment-Specific Configuration”/= Shared settingsapp = name = MyApp log_level = info
/= Development overridesdevelopment = database = host = localhost port = 5432 debug = true
/= Production settingsproduction = database = host = db.prod.internal port = 5432 debug = falseFeature Flags
Section titled “Feature Flags”features = dark_mode = true beta_dashboard = false experimental_api = false /= Enabled for gradual rollout new_onboarding = trueService Dependencies
Section titled “Service Dependencies”services = auth = url = https://auth.internal timeout = 5000 retries = 3 payment = url = https://pay.internal timeout = 10000 retries = 5 notification = url = https://notify.internal timeout = 3000 retries = 2Mixed Lists and Nested Config
Section titled “Mixed Lists and Nested Config”deployment = targets = = us-east-1 = eu-west-1 = ap-southeast-1 strategy = rolling max_unavailable = 1Common Mistakes
Section titled “Common Mistakes”Accidental Multiline Values
Section titled “Accidental Multiline Values”If a line is indented more than the previous entry, it becomes a continuation of that value, not a new entry:
/= These are TWO separate entries:host = localhostport = 8080
/= But this is ONE entry with a multiline value:host = localhost port = 8080In the second example, host has the value "localhost\n port = 8080".
Forgetting the Equals Sign
Section titled “Forgetting the Equals Sign”Every entry needs an =. A line without = is a parse error in the reference implementation --- the parser consumes the text looking for = and fails when it reaches end of input or the next valid entry:
/= This is a parse error in strict implementations:just some text
/= Always include = even for empty values:section = key = valueInline Comments
Section titled “Inline Comments”There are no inline comments in CCL. Everything after the first = is the value:
/= Do this:/= Set the portport = 8080
/= Not this (the "comment" becomes part of the value):port = 8080 /= the main portSummary
Section titled “Summary”| What you want | How to write it |
|---|---|
| Simple value | key = value |
| Nested group | parent = with indented children |
| List | = item (empty key) inside a section |
| Comment | /= text on its own line |
| Multiline value | Indent continuation lines |
| Empty value | key = (followed by nothing or a newline) |
That’s all the syntax there is. CCL’s power comes from composing these few building blocks.