Per-Site Configuration Maps in AEM: A Practical Walkthrough
Running multiple brands, regions or business units from a single Adobe Experience Manager deployment has always tested the patience of platform teams. A site manager in Sydney wants different analytics keys than a sister site in Melbourne, a regulated vertical needs its own consent thresholds, and a partner microsite may require caching rules that the main tenant cannot tolerate. Hard-coding those values into OSGi components scales poorly and creates merge conflicts that linger for months.
Context-Aware Configuration, often shortened to CA Config, solves this by binding OSGi style property maps to a path in the content tree rather than to a single global PID. The configuration map is resolved at request time through the Sling resource tree, so an author publishing under /content/sitename automatically receives the values attached to that root, while a sibling brand gets its own. It is one of the quieter features shipped by the Sling project, yet it has reshaped how AEM teams structure multi-tenant estates in production.
What Context-Aware Configuration Solves
Before CA Config existed, teams relied on run modes, environment specific overlays, or factory components with hard-coded identifiers to vary behaviour per site. Run modes work for environment differences but offer no clean way to say "this site is the UK variant of brand X". Factory PIDs can be filtered at runtime, but they expose a flat namespace and force developers to write lookup helpers that re-implement inheritance by hand.
The Sling Context-Aware Configuration API replaces these patterns with a single concept: a configuration map scoped to a resource. When a servlet, model or service runs in the context of a request, it asks the configuration resolver for a configuration interface keyed by its bundle or path. The resolver walks up the resource tree until it finds a cq:conf node containing matching properties, then returns the merged map. The result feels similar to Sling Models but for configuration rather than rendering logic.
This makes multi-brand rollouts far easier to govern, particularly for organisations subject to the Australian Privacy Principles under the Privacy Act 1988, where consent strings, retention windows and analytics endpoints must vary by jurisdiction and brand. Authors no longer need to chase developers when a new market launches; the configuration lives next to the content it serves.
Anatomy of a Configuration Map
A configuration map is an ordinary nt:unstructured or sling:Folder node named cq:conf, placed anywhere in the content hierarchy. Inside it, child nodes represent individual configuration items, with their sling:configProperty children carrying scalar values. The naming convention matches the OSGi PID of the consumer, so a feature consuming com.brand.core.services.ConsentSettings looks for a node called com.brand.core.services.ConsentSettings inside the nearest cq:conf.
Because the structure is plain JCR, version control, ACLs and package management all work without bespoke tooling. A change request can promote a new consent threshold from local to staging by promoting a content package the same way a new homepage component would be promoted. Audit trails are inherited from standard AEM workflows, which simplifies compliance reviews for teams operating under the Notifiable Data Breaches scheme.
A common pattern is to place the map at the site root (/content/brand-au/cq:conf) and then nest override maps under regional or campaign sections. Authors on the ground can adjust a single property without engineering involvement, and rollback is just a tree activation. Teams that already track deployable content with source control can map the configuration node names to feature folders, keeping the model predictable for newcomers.
Inheritance and Fallback Behaviour
Inheritance is the feature that catches most teams off guard. A configuration map at /content/brand-au automatically applies to every descendant page unless a closer map overrides it. Properties are merged at the leaf, so a child map can change a single field while inheriting the rest from the parent. This mirrors how CSS cascade works, and developers tend to grasp it quickly once they see the analogy.
Fallback behaves the same way as Sling resource fallbacks: when no map exists at the requested level, the resolver keeps walking up the tree, then falls back to the application level under /conf, and finally to the run mode specific defaults provided by the OSGi installer. The result is a layered policy stack that can be inspected, tested and overridden without redeploying code.
In practice this layered model maps well to how Australian enterprises segment their estates, with global defaults at /conf/global, regional overrides under /conf/region/anz, and brand-specific maps sitting beside the content they govern. A devops team in Brisbane can promote a global timeout change without touching any brand tree, and a brand team in Perth can ship a campaign-specific override without filing a change request.
Implementing Per-Site Maps in Code
From a consumer perspective, the API is small. A component declares an interface annotated with @ObjectClassDefinition or a plain Java interface, then injects the resolved map through @Self or the active configuration annotation. The Sling framework resolves the map relative to the current resource, which means a component rendered inside /content/brand-au/campaign/spring will see the merged map for that path.
Developers writing the consumer do not need to know where the map lives; the resolver handles that. They can still request a specific named map for edge cases by passing a configuration name to the resolver, which is useful when one component serves multiple brands from a single template. The same Java class then behaves correctly in every context it is rendered into, removing the need for brand-specific servlets.
A common companion to this approach is treating configuration maps as code for review purposes, even when they live in the JCR. Many teams export the relevant cq:conf nodes into their build pipeline as JSON and compare them during pull request review. This practice dovetails with broader AEM integration work, such as the patterns covered in Apache Thrift cross-language calls, where configuration surfaces need to agree across multiple runtimes.
Operational Concerns in Distributed AEM Environments
Once a deployment spans multiple publish farms and a content delivery network, configuration propagation deserves as much attention as content replication. A change to a consent endpoint in Sydney must reach the Melbourne publish tier before any cached page on the edge delivers the old behaviour. Standard replication can lag, especially during campaign peaks, so teams typically combine dispatcher invalidation with a Kafka or JMS hint that signals "configuration has changed" to downstream subscribers.
Performance is rarely a problem at the JCR level, but the cumulative cost of resolving many configuration maps per request can show up in profiling traces. Caching the resolved map against the resource path inside a request scoped helper avoids the repeated tree walk, particularly when Sling Models and HTL scripts call the resolver several times for the same context. A small refactor at the start of a project saves a lot of dashboard anxiety later.
Observability is another area worth investing in. Surfacing the active configuration name as a response header in non-production environments lets support engineers answer author questions without logging into the author instance. Teams already running AEM assets through cloud object stores, as described in Azure Blob asset offloading, often extend the same telemetry pipeline to track configuration drift between environments.
Integrating Context-Aware Config with Broader AEM Tooling
CA Config rarely exists in isolation. It pairs naturally with ContextHub for runtime segmentation, because both technologies key off the current resource. A persona identified by ContextHub can drive which configuration map is consulted for promotion content, which closes the loop between audience targeting and underlying service behaviour. The deep dive in ContextHub segmentation patterns explores how those two layers compose in practice.
Outside of AEM itself, configuration maps can be authored by external portals and pushed into the repository through the Sling POST servlet or a custom endpoint. This is useful for partner teams who do not have direct access to the AEM author but still need to manage their own settings. A small GitOps style workflow, with pull requests landing in a repository that gets synced into the JCR, fits Australian devops teams well, especially those running AARNet connected research infrastructure or government tenants with strict change windows.
Finally, it is worth treating configuration maps as a documented contract between engineering and content teams. The shape of the interface, the supported override layers and the validation rules belong in the same playbook as component APIs. Teams that invest in that documentation tend to ship multi-brand work faster, because everyone understands which decisions belong in code and which belong in the tree. For practical inspiration on building those shared rituals, the Linux admin resources offered by the broader community often discuss similar boundary questions that translate well to AEM estates, and conference goers planning a trip might also find a Showboat Hotel packing checklist handy for keeping laptops and accessories safe on the road.
If your team is wrestling with multi-brand AEM deployments, now is the right moment to map the existing configuration footprint, identify the per-site properties that change most often, and prototype a configuration map tree before the next release. Reach out to the CIRCUIT community to share what you learn, propose a session for the next gathering, or browse the recordings from previous years to see how other architects have tackled the same problem.