Skip to main content


Speed Up AEM Developer Onboarding with Container-Driven Local Instances

Business People Negotiating A Contract.

Customization is a key aspect of any AEM-powered solution. Installing AEM instances on developers’ local machines is usually required and recommended by Adobe. This allows for the fast creation of components, templates, and other project-specific items.

The local AEM instances mimic an organization’s integration or dev environment instances with custom patch levels, OSGi configurations, data storage topologies, etc.

Standing up new local instances that are on par with an organization’s specific requirements takes time. Individuals new to AEM often run into challenges when first setting up their local environments. To alleviate these issues, a repeatable process can be used to speed up local AEM installations, ensure parity with an organization’s instances, and provide framework stability. With AEM being accessed primarily through a browser, containers are ideal for this.

In this post, I’ll walk through every detail of setting up AEM in containers using our sample GitHub project.

6 Goals for the Repeatable Process

  1. Automate the creation of AEM instances
  2. Ensure environment consistency among all developers in a project
  3. Automate provisioning of organization-specific configurations to those AEM instances
  4. Simplify DAM assets usage in local instances
  5. Allow for tear-down and rebuild of the instances
  6. Allow for easy migration between devices

Automate the Creation of AEM Instances and Provisioning of Configurations

While there are other options, I use Docker as my container engine since it has broad support for the major OS platforms. To use our sample project, you will need Docker installed and your own AEM file (place it in the aem-base/packages folder). Apart from that, the only other requirements are that you run this on a multi-core system with at least 12GB of RAM (16+ is preferred) and you have the bash shell available (OSX, Linux and newer Windows versions have this available out of the box).

With those prerequisites in place, let’s go over the build process and then run the script that puts it all together.

Files Used to Build the Containers

A docker-compose.yml file
This file is used as the IaC configuration for building all the needed infrastructure and providing parameters used in the building of the containers.

A Dockerfile (one for the Author and Publisher, and one for the Dispatcher)
This file defines the configuration of the images the containers are created from.
This provides commands executed immediately after the Author and Publish containers are started. This file starts the AEM JVM.

Specific to AEM, there are also replication packages used to configure replication and flush agents between the author and publisher.

You may include several other optional packages. These are used to tailor the container builds to an organization’s requirements. Among them include AEM service packs, DAM mappings (more on this later), core components, and anything else needed. OSX, Linux, and newer Windows versions have this out of the box

The docker-compose file begins the creation process by configuring volumes for the containers. These volumes are crucial to persisting changes in your AEM local repositories between restarts. The volumes store the actual crx-quickstart contents locally and maps them to locations in the containers.

Then, the docker-compose file sets up a subnet in the docker internal network. The containers are assigned IP addresses in this subnet.

Finally, the docker-compose file invokes the Docker engine to create the containers.

The process of building the containers requires base images to build from. These base images provide a Linux environment for the java runtime and AEM repository. There are many Linux container variants available in Docker’s registries for this and you can also host your own in a private registry.

This base image is provided in the Dockerfiles via these statements:
FROM ubuntu:latest

The aem-base and aem-dispatcher images inherit from the Linux base images.

Dockerfile Details

The Dockerfile in the aem-base folder defines the specifics of the Author and Publisher images, namely run modes to set, the Java JDK version installed and used, included packages, exposed ports, and lastly the installation and execution of the script.

Regarding included packages, our project includes the latest AEM 6.5 service pack (as of this writing that’s 6.5.14), replication agent configurations, and a DAM mapping package.

The packages are stored in the aem-base/packages folder, you can add or remove any number of packages you want to include in the AEM builds, custom to your needs. These may be content packages, Sling configurations, Core Components, etc.

The packages are included in the container builds via these lines in the Dockerfile:

RUN mkdir -p /data/aem/crx-quickstart/install
ADD packages/ 

You can add additional packages by updating this file to include them before the /data/aem/crx-quickstart/install/ line:

RUN mkdir -p /data/aem/crx-quickstart/install
ADD packages/ 

Assuming you update our sample project by adding your custom packages and make it available through your source control tooling, anything you add will be included for anyone who uses the project to set up their local environment, meaning everyone gets a consistent, organization-specific configuration.

The Dockerfile in the aem-dispatcher directory sets up a container with Apache installed and adds the dispatcher module (as of this writing, it’s using version 4.3.5).

To upgrade the dispatcher version, you just need to modify the DISPATCHER_MODULE_URL argument to point to the new version’s download URL and then rebuild the aem-dispatcher container.

Simplify DAM Assets Usage in Local Instances

I mentioned the use of DAM mappings and a local-dam-mapping package is included among the list of packages in the sample project.

A major challenge for local development is keeping in sync with the Assets implementation(s). As the Assets change and the size of DAM locations increases it becomes more and more difficult to manage the moving of this content via packages or through tools like vlt. It would be better to have a process that requires no content ingestion to the local instances but still allows these assets to be rendered in AEM components. This is where the DAM mapping package comes in.

This package contains a simple Sling mapping that transforms /content/dam subresource requests in rendered Sites pages, Experience Fragments, or Content Fragments to point to the same resources under one of your site domains. This means that when a page is loaded in your local AEM author or publisher, images are rendered automatically, without the need for them to be installed in your local repository.

To use it, you need to modify the jcr_root/etc/map/local-dam-mapping/.content.xml file (in the local-dam-mapping package) to add your specific DAM application path and domain in the sling:match and sling:redirect properties.

As an example, If you have a domain of and you have assets under /content/dam/weretail, then .content.xml from our sample project needs to be updated from this:

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="" xmlns:jcr=""
    sling:match="[^/]+/[^/]+/content/dam/&lt;your application name here>(.*)$"
    sling:redirect=";your application name here>$1"

to this:

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="" xmlns:jcr=""

Note, it’s recommended to use your production domain if you’re following the recommended practice of having AEM content start life in production, but you don’t have to. An author domain, Assets-specific instance hostname, or other URL containing your assets can be used in the sling:redirect property. Also, you can modify the /content/dam path to any repository location providing your assets.

Multiple mappings using this pattern will allow you to rewrite Asset requests among multiple DAM repository locations.

Bear in mind, with this approach the assets are requested but not stored in the local repository.

If you are doing any work directly with Assets, rendition workflows, metadata schemas, and the like you will want to install at least some sample assets in your local environment for testing.

Allow for Tear-Down and Rebuild of the Instances

In our sample project, the script puts everything discussed above together. Running this script is all you need to do to build the author, publisher, and dispatcher. If you’ve added your own file and custom packages (and DAM mapping packages), new developers simply need to install Docker, clone the project, and run When the script completes execution, they’ll have a local AEM environment, patched and ready for immediate use.

Two optional arguments can be supplied when running this script:

  1. Wait time (in minutes)
  2. resetaem – used to reset and rebuild the environment

The wait time defines how long to wait after the initial start of AEM before restarting (restarts are done as part of the setup process). If no value is supplied, the default is 30 minutes. Why? Because AEM takes time to initialize the first time it runs, this is especially true if using the Forms add-on. In fact, Adobe engineers advise waiting up to an hour when a new instance is started with the Forms add-on.

This is only needed for the first-time startup. The script skips the wait time if the instances already exist and just need to be started.

The resetaem argument must always come after the wait time and should be used with caution. It will destroy the existing docker containers, images, volumes, and network before rebuilding them fresh and new.

Allow for Easy Migration Between Devices

When developers upgrade or change devices, they can clone this project on the new device and run the script to have their local AEM environments up and running with one important caveat. Remember, AEM data is persisted between container restarts via the volumes. These volumes need to be moved to the new device if you wish to migrate existing data created in the local containers.

Any packages committed under aem-base/packages and included in the Dockerfile will always have their content included wherever the project is cloned and built, regardless of whether the volumes are moved or not. If you’re following the best practice of committing code to a source control system like Azure DevOps Repositories or BitBucket, you’ll likely not need to retain volume data. Such projects can easily be cloned and installed on a fresh build of the containers.

You’re On Your Way to Faster AEM Installations

You now have a repeatable process to empower your team with rapid, automated provisioning of developer instances using containers and our sample GitHub project. If you need more support for your AEM project, connect with us! We’re certified by Adobe for our proven capabilities, and we hold an Adobe Experience Manager specialization (among others).

And check out our other AEM blogs for more tips and tricks to help you get the most out of your AEM investment.

Thank you for reading!

Continue Learning

How to Dynamically Customize Your AEM Component Toolbar / Blogs / Perficient

AEM Author Notifications Made Easy / Blogs / Perficient

AEM Forms: Custom Styling for the AEM Forms Component / Blogs / Perficient

AEM Customization: Show Unpublished Reference Alert for Tagpicker / Blogs / Perficient


Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Josiah Huckins

I have years of experience in web application support and development, with emphasis on the AEM Framework and Java. I like to write about anything AEM related (Sling, OSGi, the JCR, Dispatcher, etc.).

More from this Author

Follow Us