Why Oak Indexes Need Conversion
What Are Oak Indexes?
Oak indexes are the backbone of AEM's query performance. When you run a query like
SELECT * FROM [cq:Page] WHERE [jcr:content/cq:template] = '/conf/mysite/templates/page',
Oak uses indexes to avoid scanning the entire repository.
In AEM 6.5, you likely have custom Lucene indexes defined under /oak:index with properties
like includedPaths, excludedPaths, custom analyzers, and aggregation rules.
These were deployed via content packages or manually created in CRXDE.
Why AEM Cloud Has Stricter Rules
AEM as a Cloud Service fundamentally changes how indexes are managed:
- Immutable repository — You can't create or modify indexes at runtime
- Deployment validation — Cloud Manager rejects deprecated properties and unsupported configurations
- Standardized structure — Indexes must be deployed as content packages with specific JCR paths
- No custom analyzers — Many AEM 6.5 analyzer configurations aren't supported
If you push your AEM 6.5 indexes directly to Cloud Manager, the build will fail with cryptic validation errors.
What the Index Converter Does (and Doesn't Do)
Transforms index definitions to Cloud-compatible format. Removes deprecated properties. Restructures paths. Generates deployment-ready content packages.
It won't fix your queries. It won't optimize your indexes. It won't tell you if your indexes are even being used. You still need to validate query performance after conversion.
Prerequisites
Required Tools
- Node.js (v18+ recommended)
- npm
- Adobe aio-cli (Adobe I/O CLI)
- aem-cloud-migration plugin
Install Adobe aio-cli
# Install Adobe I/O CLI globally
npm install -g @adobe/aio-cli
# Verify installation
aio --version
Install the AEM Cloud Migration Plugin
# Install the migration plugin
aio plugins:install @adobe/aio-cli-plugin-aem-cloud-migration
# Verify it's installed
aio plugins
You should see @adobe/aio-cli-plugin-aem-cloud-migration in the list.
Confirm Plugin Commands Are Available
# List available commands
aio aem-cloud-migration --help
Look for index-converter in the command list.
The global aio-cli config file (~/.config/@adobe/aio-cli/config.yaml) is NOT the
same as the index converter config. The converter uses its own YAML file that you create
specifically for the conversion. Don't confuse the two.
Demo Setup Using WKND
We'll use the WKND sample project. It's better maintained than We.Retail and mirrors real-world AEM project structures.
Clone and Build WKND
# Clone WKND
git clone https://github.com/adobe/aem-guides-wknd.git
cd aem-guides-wknd
# Build (skip tests for speed)
mvn clean install -DskipTests
Create a Custom Index Folder
WKND doesn't have custom indexes by default. Let's create a sample to demonstrate the conversion process.
# Create the _oak_index folder structure
mkdir -p ui.apps/src/main/content/jcr_root/_oak_index/wknd-content-index
Add a Sample Index Definition
Create the .content.xml file for your index:
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0"
xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
xmlns:oak="http://jackrabbit.apache.org/oak/ns/1.0"
jcr:primaryType="oak:QueryIndexDefinition"
type="lucene"
async="async"
compatVersion="{Long}2"
includedPaths="[/content/wknd]"
queryPaths="[/content/wknd]"
evaluatePathRestrictions="{Boolean}true">
<indexRules jcr:primaryType="nt:unstructured">
<nt:unstructured jcr:primaryType="nt:unstructured">
<properties jcr:primaryType="nt:unstructured">
<jcrTitle
jcr:primaryType="nt:unstructured"
name="jcr:content/jcr:title"
propertyIndex="{Boolean}true"/>
<cqTemplate
jcr:primaryType="nt:unstructured"
name="jcr:content/cq:template"
propertyIndex="{Boolean}true"/>
</properties>
</nt:unstructured>
</indexRules>
</jcr:root>
File Structure After Setup
aem-guides-wknd/
├── ui.apps/
│ └── src/
│ └── main/
│ └── content/
│ ├── jcr_root/
│ │ └── _oak_index/
│ │ └── wknd-content-index/
│ │ └── .content.xml
│ └── META-INF/
│ └── vault/
│ └── filter.xml
Update filter.xml to Include Index Path
Add the index path to your filter.xml:
<?xml version="1.0" encoding="UTF-8"?>
<workspaceFilter version="1.0">
<!-- Existing filters -->
<filter root="/apps/wknd"/>
<!-- Add this line for oak indexes -->
<filter root="/_oak_index/wknd-content-index"/>
</workspaceFilter>
Create Index Converter Config YAML
The Index Converter needs a configuration file telling it where to find your indexes and filter.xml.
Full Config File
Create index-converter.yaml in your project root:
# AEM version of your source instance
aemVersion: "6.5"
# Path to your custom oak index definitions
# This is where your _oak_index folder lives
customOakIndexDirectoryPath: "/Users/yourname/aem-guides-wknd/ui.apps/src/main/content/jcr_root/_oak_index"
# Path to filter.xml (must be a FILE, not directory)
filterXMLPath: "/Users/yourname/aem-guides-wknd/ui.apps/src/main/content/META-INF/vault/filter.xml"
# (Optional) Where to output the converted index definitions
# If not set, converter creates folders in current directory
indexConversionDefinitionContentPackageJcrRootPath: "/Users/yourname/aem-guides-wknd/ui.apps/src/main/content/jcr_root"
# (Optional) Use the same path for config package output
useIndexDefinitionConfigPackageJcrRootPath: "/Users/yourname/aem-guides-wknd/ui.apps/src/main/content/jcr_root"
Config Field Reference
| Field | What It Does |
|---|---|
aemVersion |
Source AEM version. Use "6.5" for AEM 6.5.x. Do NOT use "6.4" unless you're actually on 6.4. |
customOakIndexDirectoryPath |
Absolute path to your _oak_index or oak:index folder containing index definitions. |
filterXMLPath |
Absolute path to filter.xml FILE (not directory). Must end with filter.xml. |
indexConversionDefinitionContentPackageJcrRootPath |
Where to write the converted index definition content package. |
useIndexDefinitionConfigPackageJcrRootPath |
Where to write the converted index config package. |
How to Find the Right Paths in Your Repo
Locate filter.xml
# Find all filter.xml files in your project
find . -name "filter.xml" -type f
# Typical locations:
# ./ui.apps/src/main/content/META-INF/vault/filter.xml
# ./ui.content/src/main/content/META-INF/vault/filter.xml
Locate Oak Index Content
# Find _oak_index folders
find . -type d -name "_oak_index"
# Find oak:index folders (may be named differently)
find . -type d -name "oak*index*"
# Search for index definitions by content
grep -r "oak:QueryIndexDefinition" --include="*.xml" .
Verify filter.xml Includes Index Paths
# Check what paths are in filter.xml
cat ui.apps/src/main/content/META-INF/vault/filter.xml | grep "filter root"
Make sure your _oak_index path is included. If not, add a filter entry for it.
Run the Index Converter
Correct Command Syntax
The correct command uses spaces, not colons. The old colon-style
(index-converter:convert) is deprecated and may not work.
# Navigate to your project root
cd /Users/yourname/aem-guides-wknd
# Run the converter
aio aem-cloud-migration index-converter --config ./index-converter.yaml
Expected Output
Index Converter starting...
Reading config from ./index-converter.yaml
AEM Version: 6.5
Processing custom index definitions from: /Users/yourname/aem-guides-wknd/ui.apps/src/main/content/jcr_root/_oak_index
Converting index: wknd-content-index
- Removed deprecated property: compatVersion
- Updated index structure for Cloud compatibility
Writing converted indexes to: /Users/yourname/aem-guides-wknd/ui.apps/src/main/content/jcr_root
Conversion complete.
Indexes converted: 1
Warnings: 0
Errors: 0
Where Results Are Written
The converter creates or modifies files in your jcr_root directory. Look for:
_oak_index/— Updated index definitions- New
.content.xmlfiles with Cloud-compatible properties - Removed deprecated nodes and properties
Validate the Results
What Files/Folders Changed
# See what the converter modified
git status
# Diff a specific index
git diff ui.apps/src/main/content/jcr_root/_oak_index/wknd-content-index/.content.xml
Common Warnings and What They Mean
| Warning | Meaning |
|---|---|
Unsupported analyzer |
Custom Lucene analyzer not supported in Cloud. Will use default analyzer. |
Deprecated property removed |
Property like compatVersion was removed as it's not used in Cloud. |
Index path restructured |
Index moved to Cloud-compatible path structure. |
Aggregate rule modified |
Aggregation configuration adjusted for Cloud compatibility. |
What to Check Into Git
- Updated
.content.xmlfiles in_oak_index - Any new folders created by the converter
- Updated
filter.xmlif paths changed - Do NOT check in the converter config file (it has local paths)
How to Re-Run Safely
The converter is idempotent—you can run it multiple times:
# Re-run after making changes
aio aem-cloud-migration index-converter --config ./index-converter.yaml
# If you need a fresh start, reset your changes first
git checkout -- ui.apps/src/main/content/jcr_root/_oak_index/
Troubleshooting
Error A: "index-converter:convert is not a aio command"
$ aio index-converter:convert --config ./config.yaml
index-converter:convert is not a aio command.
Did you mean aem-cloud-migration index-converter?
Fix: Use the space-separated command format:
# Wrong (old/deprecated)
aio index-converter:convert --config ./config.yaml
# Correct
aio aem-cloud-migration index-converter --config ./config.yaml
Error B: "Unable to read baseline.xml for AEM Version 6.4"
Error: Use AEM Version for Reference 6.4
Unable to read baseline.xml for AEM Version 6.4
Fix: Set the correct AEM version in your config:
# In your index-converter.yaml
aemVersion: "6.5" # NOT "6.4" unless you're actually on 6.4
The converter uses baseline definitions for each AEM version. If you specify the wrong version, it can't find the reference files.
Error C: "cat /home/develop/.config/@adobe/aio-cli/config.yaml: No such file"
Error: cat /home/develop/.config/@adobe/aio-cli/config.yaml: No such file or directory
Fix: This is a HOME directory issue. The error is misleading—it's looking for the global aio config, not your converter config.
# Check your HOME is set correctly
echo $HOME
# The global aio config location varies:
# macOS: ~/.config/@adobe/aio-cli/
# Linux: ~/.config/@adobe/aio-cli/
# This is NOT your index-converter.yaml
# Your converter config is a separate file you create
Make sure you're passing the correct path to YOUR converter config file, not the aio global config.
Error D: "cd .../META-INF/vault/filter.xml: Not a directory"
Error: cd /Users/yourname/project/ui.apps/src/main/content/META-INF/vault/filter.xml: Not a directory
Fix: The filterXMLPath must be a FILE path, not a directory:
# Wrong - pointing to directory
filterXMLPath: "/path/to/META-INF/vault/"
# Correct - pointing to the file
filterXMLPath: "/path/to/META-INF/vault/filter.xml"
This error can also occur with older plugin versions. Try upgrading:
aio plugins:update
aio plugins:install @adobe/aio-cli-plugin-aem-cloud-migration@latest
General Fix: Upgrade the Plugin
Many issues are resolved by updating to the latest plugin version:
# Update all plugins
aio plugins:update
# Or reinstall the specific plugin
aio plugins:uninstall @adobe/aio-cli-plugin-aem-cloud-migration
aio plugins:install @adobe/aio-cli-plugin-aem-cloud-migration@latest
# Verify version
aio plugins
Next Steps
Validate Queries with Oak Tools
After conversion, verify your queries actually use the indexes. In AEM:
- Go to Tools → Operations → Diagnosis → Query Performance
- Run your queries and check the "Index Used" column
- Queries showing "traversal" need index optimization
Deploy via Cloud Manager
- Commit your converted indexes to Git
- Push to your Cloud Manager repository
- Run a build pipeline—it should pass validation now
- Deploy to dev environment first
Coordinate with Performance Testing
- Run load tests after deploying new indexes
- Monitor index reindexing time (can take hours for large repositories)
- Watch for query performance regressions
Multi-Repo Architecture: Centralize Index Ownership
If you have multiple repositories feeding into an aggregator:
- Keep all index definitions in ONE repository (usually the aggregator)
- Don't let individual tenant repos define their own indexes
- Use naming conventions to identify index ownership
- Document which indexes support which queries
Create a README.md in your _oak_index folder documenting each index:
what queries it supports, who owns it, and when it was last modified. Future you will thank you.
Related Topics
Need Help with AEM Cloud Migration?
Index conversion is just one piece of the puzzle. Our team has migrated enterprise AEM implementations to the cloud—let us help you avoid the pitfalls.
Get Expert Help