HCL Configuration
Write docker-agent configs in HCL instead of YAML. It maps to the same docker-agent schema and validation rules.
docker-agent supports .hcl config files anywhere it supports .yaml or .yml files. HCL is useful if you prefer labeled blocks, less punctuation, and heredocs for long prompts.
YAML and HCL are just two syntaxes for the same docker-agent configuration model. docker-agent converts HCL to the equivalent YAML structure internally, then runs the normal schema validation and loading pipeline.
Minimal Example
#!/usr/bin/env docker agent run
agent "root" {
model = "openai/gpt-5-mini"
description = "A helpful assistant"
instruction = <<-EOT
You are a helpful assistant.
EOT
toolset "think" {}
}
Run it exactly like a YAML config:
$ docker agent run agent.hcl
$ docker agent run --exec agent.hcl "Summarize this repository"
$ docker agent serve api ./agents/ # directories may mix .yaml, .yml, and .hcl files
HCL changes the syntax, not the meaning of fields. For what each field does, see Agent Config, Model Config, and Tool Config.
YAML vs HCL
These two configs are equivalent:
models:
claude:
provider: anthropic
model: claude-sonnet-4-5
agents:
root:
model: claude
description: Coding assistant
instruction: You help with software development.
toolsets:
- type: filesystem
- type: shell
model "claude" {
provider = "anthropic"
model = "claude-sonnet-4-5"
}
agent "root" {
model = "claude"
description = "Coding assistant"
instruction = "You help with software development."
toolset "filesystem" {}
toolset "shell" {}
}
Core Conventions
HCL follows a few simple mapping rules:
| HCL syntax | YAML shape |
|---|---|
agent "root" { ... } |
agents.root |
model "claude" { ... } |
models.claude |
provider "team" { ... } |
providers.team |
mcp "github" { ... } |
mcps.github |
rag "docs" { ... } |
rag.docs |
command "fix" { ... } inside an agent |
commands.fix |
toolset "shell" {} |
list item in toolsets with type: shell |
metadata { ... }, permissions { ... } |
singleton blocks with the same top-level name |
Top-level keyed maps become labeled blocks
In YAML, several sections are maps keyed by name. In HCL, those become labeled blocks:
model "claude" {
provider = "anthropic"
model = "claude-sonnet-4-5"
}
agent "root" {
model = "claude"
description = "Primary assistant"
instruction = "You are helpful."
}
The supported top-level labeled blocks are:
agentmodelprovidermcprag
The supported top-level singleton blocks are:
metadatapermissions
Toolsets use the block label as type
Instead of writing list entries with type: ..., HCL uses a toolset block whose label becomes the tool type:
agent "root" {
model = "openai/gpt-5-mini"
description = "Dev assistant"
instruction = "You can inspect and modify code."
toolset "filesystem" {}
toolset "mcp" {
ref = "docker:github-official"
}
}
Commands use labeled blocks too
Agent commands are often nicer to write in HCL because each command gets its own block:
agent "root" {
model = "openai/gpt-5-mini"
description = "Build helper"
instruction = "You help with builds."
command "fix-lint" {
description = "Fix lint issues"
instruction = "Run the linter, then fix any problems."
}
}
Strings and Heredocs
Use quoted strings for short values and heredocs for long prompts, welcome messages, or embedded JSON.
agent "root" {
model = "openai/gpt-5-mini"
description = "Friendly assistant"
instruction = <<-EOT
You are a helpful assistant.
Keep answers concise and practical.
EOT
}
Escaping literal ${...}
HCL treats ${...} inside strings and heredocs as template interpolation. If you need the literal text ${...} in your prompt, escape it as $${...}.
This matters for command prompts that intentionally show docker-agent template snippets:
command "fix-lint" {
instruction = <<-EOT
Run the linter and inspect the result:
$${shell({cmd: "task lint"})}
EOT
}
The model will receive the literal ${shell({cmd: "task lint"})} text.
Repeated Blocks Become Lists
Some YAML sections are lists. In HCL, those are written as repeated blocks.
For example, model routing rules become repeated routing { ... } blocks:
model "smart_router" {
provider = "openai"
model = "gpt-5-mini"
routing {
model = "anthropic/claude-sonnet-4-5"
examples = [
"Write a detailed technical document",
"Review this code for security issues",
]
}
routing {
model = "openai/gpt-5"
examples = [
"Generate some creative ideas",
"Help me brainstorm",
]
}
}
The same idea applies to other list-shaped sections such as RAG strategy blocks and hook event entries.
Important Differences from Terraform
docker-agent uses HCL as a configuration syntax, not as Terraform:
- There are no modules,
locals, orvariableblocks. - There are no docker-agent-specific HCL functions to call from expressions.
- Prefer normal literal values: strings, numbers, booleans, lists, objects, and nested blocks.
- After conversion, the result is validated exactly like the equivalent YAML config.
If you already know Terraform, think of docker-agent HCL as a thin block-based syntax over the existing config schema.
Examples
See these real configs in the repository: