How @id Fields Work in Caddy Config: Complete Guide to Meta-Field Indexing
Caddy treats @id as a meta-field that assigns stable identifiers to JSON objects, enabling convenient /id/<identifier> admin API shortcuts while stripping the field before runtime configuration loading.
Caddy's configuration system uses @id fields to create durable references to configuration objects without polluting the final Config structure. According to the caddyserver/caddy source code, these meta-fields enable stable addressing through the admin API while remaining transparent to module provisioning and validation. Understanding how Caddy @id fields work is essential for managing complex deployments programmatically.
What Are @id Fields in Caddy Configuration?
In the Caddy architecture, an @id field acts as a meta-field that can be attached to any JSON object within a configuration. The field serves three primary purposes: it assigns a stable identifier to config objects, builds an in-memory index mapping identifiers to their JSON paths, and exposes a shorthand admin API endpoint at /id/<identifier>.
Critically, @id does not become part of the final runtime Config structure. The field is stripped during the loading phase so it never interferes with module provisioning or validation.
The @id Field Lifecycle
The transformation of @id from configuration text to usable API reference follows a strict pipeline across two core files: caddy.go and admin.go.
Loading and Meta-Field Stripping
When Caddy loads configuration, it first sanitizes the raw JSON to remove meta-fields before unmarshaling. The RemoveMetaFields function in admin.go (line 1325) handles this preprocessing.
This function uses a pre-compiled regular expression defined as idRegexp in admin.go (line 1444) to match and remove @id entries and their surrounding commas. This stripping prevents the JSON decoder from encountering unknown fields that would cause unmarshaling errors.
Configuration Parsing
After meta-field removal, the cleaned JSON proceeds to unsyncedDecodeAndRun in caddy.go (lines 313-324). Here, the sanitized configuration is unmarshaled into a *Config structure. Because @id has already been removed, the parser encounters only valid configuration fields recognized by Caddy's module system.
Index Building
Once loaded, Caddy recursively traverses the configuration to build an addressable index. The indexConfigObjects function in caddy.go (lines 275-294) performs this traversal, recording every object containing an @id key in the rawCfgIndex map.
This index stores mappings of identifier → expanded JSON-path. During indexing, Caddy enforces uniqueness constraints—duplicate @id values within the same configuration trigger an error. Both string and numeric values are accepted for @id, though numbers are internally stringified when stored in the index.
API Resolution
When admin API requests arrive at the /id/<identifier> endpoint, the handleConfigID function in admin.go (lines 1093-1126) processes them. This handler looks up the identifier in rawCfgIndex and internally rewrites the request to the corresponding /config/... path, supporting GET, POST, and PATCH operations.
The literal string "@id" is defined as the constant idKey in admin.go (line 1458), ensuring consistent reference across the codebase.
Practical Usage of @id Fields
You can embed @id fields in both native JSON configurations and Caddyfile syntax.
JSON Configuration Example
In raw JSON, attach @id to any object to assign it a stable reference:
{
"apps": {
"http": {
"@id": "my_http_app",
"servers": {
"example": {
"@id": 100,
"listen": [":80"],
"routes": [
{
"handle": [
{
"handler": "static_response",
"body": "Hello, world!"
}
]
}
]
}
}
}
}
}
Caddyfile Syntax
The Caddyfile adapter supports @id through the @id <value> directive:
{
apps {
http @id my_http_app {
servers {
example @id 100 {
listen :80
routes {
respond "Hello, world!"
}
}
}
}
}
}
Admin API Access
Once indexed, access objects directly via their identifiers using the /id/ endpoint:
# Retrieve the HTTP app configuration using its string ID
curl -s http://localhost:2019/id/my_http_app
# Access the server object using its numeric ID (converted to string "100")
curl -s http://localhost:2019/id/100
These requests resolve to their full JSON paths—/config/apps/http and /config/apps/http/servers/example respectively—while returning the exact object contents.
Technical Constraints and Requirements
When implementing @id fields in Caddy config, observe these constraints defined in the source code:
- Uniqueness: Identifiers must be unique across the entire configuration; duplicates cause indexing failures
- Type flexibility: Values may be strings or numbers, but numbers are stringified internally
- Volatility: The
rawCfgIndexexists only in memory; you must persist@idfields in your source configuration to maintain identifiers across restarts - Scope:
@idcan be attached to any JSON object, regardless of nesting depth or module type
Summary
- @id fields in Caddy config serve as meta-fields that provide stable identifiers without affecting runtime configuration structure
- The loading pipeline strips
@idviaRemoveMetaFieldsinadmin.gobefore unmarshaling, then builds an index viaindexConfigObjectsincaddy.go - The admin API exposes a shorthand
/id/<identifier>endpoint handled byhandleConfigIDinadmin.gothat resolves to full JSON paths - Values can be strings or numbers, must be unique, and are stored in the in-memory
rawCfgIndexmap - Both JSON and Caddyfile formats support
@idsyntax for flexible configuration management
Frequently Asked Questions
Can I use @id in the Caddyfile, or is it limited to JSON?
You can use @id in both formats. In JSON, include it as a standard object key. In the Caddyfile, use the @id <value> directive immediately after the block opening. The Caddyfile adapter converts these directives to JSON meta-fields during the adaptation phase, resulting in identical runtime behavior.
What happens if I accidentally use duplicate @id values?
Caddy enforces uniqueness during the indexing phase in indexConfigObjects. If duplicate identifiers are detected, Caddy returns an error and refuses to load the configuration. This prevents ambiguous API resolution where multiple objects might claim the same /id/<identifier> endpoint.
Do @id fields persist after Caddy restarts?
No. The rawCfgIndex exists only in memory while Caddy runs. To maintain stable identifiers across restarts, you must preserve the @id fields in your persistent configuration source (JSON file, Caddyfile, or config database). When Caddy reloads, it rebuilds the index from scratch based on the current configuration.
Can @id values contain special characters or spaces?
While the source code stringifies numeric values and stores identifiers as map keys, you should generally use URL-safe characters since these values become path segments in the admin API (/id/<identifier>). Avoid spaces and special URL-encoded characters to ensure predictable API behavior.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →