Create AEM in Docker with Docker Compose.

AEM (Adobe Experience Manager) is a Content Management System that is widely used in large organizations. WordPress, Drupal, and much other Content Management Systems are good enough for developing and serving the Web content. But AEM is leading with Comprehensive and Robust Content Management System. However, the application runtime platform of AEM is very huge and complex to set up every time compared to other CMS Platforms. Therefore, just like any other application platform, containerization of this application will make provisioning and Infrastructure deployment process easier. So, in this article, we will discuss How to Create AEM instance in Docker with Docker Compose.

Plan of action

Before starting, lets see our plan of action. We are going to create the following components.

  • AEM Instances
    • Author – Runs on Port 4502
    • Publisher – Runs on Port 4503
    • Dispatcher – Runs on Port 80
  • Separate folder for all the three instances with AEM binary and License files.
  • Each folder will act as separate Docker Images
  • docker-compose.yml file to compose the Images to run together as containers
Need of Containers
Need of Containers

Setting Up Folder Structures required files.

As per our plan of action, we are going to create three different AEM instances, Author, Publisher, Dispatcher respectively. For the same, we will create four folders namely, aem-base_image, aem-author, aem-publisher, aem-dispatcher.

$ tree aem-in-docker/
aem-in-docker/
├── aem-author
├── aem-base_image
├── aem-dispacher
└── aem-publisher

Creating Base Image

In this, “aem-base_image” folder will have the Dockerfile and AEM binary including licenses file to create base AEM Docker image with your own license. So do the following steps.

  • Keep the cq-quickstart-6.5.0.jar file and license.properties in the aem-base_image folder.
  • Create Dockerfile and add the following lines in it.
# Using 'ubuntu' as the base image here
FROM ubuntu

# setting the working directory
WORKDIR /opt/aem

# Copy the license file
COPY license.properties .

# Copy Quickstart jar file
COPY cq-quickstart-6.5.0.jar cq-quickstart.jar

# Install Java
RUN apt-get update && \
    apt-get install -y curl && \
    apt-get install -y software-properties-common && \
    add-apt-repository ppa:openjdk-r/ppa && \
    apt-get update && \
    apt-get install -y openjdk-8-jdk

# Unack the Jar file
RUN java -jar cq-quickstart.jar -unpack
  • Now, Build the Base Image by assing the following command.
$ docker build -t aem-base-image .

Now, If you see the list of docker images, you can see the created docker image aem-base-image.

$ sudo docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
aem-base-image      latest              86ca799058b5        56 seconds ago      5.57MB
alpine              latest              a24bb4013296        2 months ago        5.57MB

So, now you can either push it to your docker registry or keep it in local.

Dockerfile for AEM Author

Now, Inside the aem-author folder, create a Dockerfile and put the following line of code.

# Use the previously created aem-base-image
FROM aem-base-image

# Expose AEM author in port 4502
EXPOSE 4502

# Make the container start always in Author mode with Port 4502
ENTRYPOINT ["java", "-jar", "cq-quickstart.jar", "-Dsling.run.modes=author", "-p", "4502"]

IMPORTANT NOTE:

To Run the AEM Instance, You need Minimum 2GB Memory and 5GB Disk Space. However, This tutorial may ask you to create 3 or more containers running parallelly which demand a minimum of 8GB RAM configuration If you are trying in your Personal Computer.

If you want to run the only the Author, You can stop here and build this Dockerfile to create author docker image and run the container out of it. For the same, Do the following steps.

$ docker build -t aem-author .
$ docker run -p 4502:4502 aem-author

Now, Your AEM Author will be running on http://localhost:4502

NOTE:

If you want to Setup Publisher too, Continue the Steps.

Dockerfile for AEM Publisher.

Creating Docker Image and container for the AEM publisher is as same as AEM Author. So Just do all the steps except exposing the port number 4503 instead of Port 4502. So, the complete code of Dockerfile is below.

# Use the previously created aem-base-image
FROM aem-base-image

# Expose AEM Publisher in port 4503
EXPOSE 4503

# Make the container start always in Publisher mode with Port 4503
ENTRYPOINT ["java", "-jar", "cq-quickstart.jar", "-Dsling.run.modes=publisher", "-p", "4503"]

Then, you can build and run using the following command.

$ docker build -t aem-publisher .
$ docker run -p 4503:4503 aem-publisher

Now, If you want to stop with creating Author and Publisher, you can stop here and access you publisher in  http://localhost:4503.

Dockerfile for AEM Dispacher

So, Our plan is to run the Dispatcher is with the Apache application server. So, We can start with the Apache configuration file. Hence, we will create a file called httpd.conf and the following content inside this

ServerRoot "/usr/local/apache2"
Listen 80
LoadModule mpm_event_module modules/mod_mpm_event.so
LoadModule authn_file_module modules/mod_authn_file.so
LoadModule authn_core_module modules/mod_authn_core.so
LoadModule authz_host_module modules/mod_authz_host.so
LoadModule authz_groupfile_module modules/mod_authz_groupfile.so
LoadModule authz_user_module modules/mod_authz_user.so
LoadModule authz_core_module modules/mod_authz_core.so
LoadModule access_compat_module modules/mod_access_compat.so
LoadModule auth_basic_module modules/mod_auth_basic.so
LoadModule reqtimeout_module modules/mod_reqtimeout.so
LoadModule filter_module modules/mod_filter.so
LoadModule mime_module modules/mod_mime.so
LoadModule log_config_module modules/mod_log_config.so
LoadModule env_module modules/mod_env.so
LoadModule headers_module modules/mod_headers.so
LoadModule setenvif_module modules/mod_setenvif.so
LoadModule version_module modules/mod_version.so
LoadModule unixd_module modules/mod_unixd.so
LoadModule status_module modules/mod_status.so
LoadModule autoindex_module modules/mod_autoindex.so

<IfModule !mpm_prefork_module>
</IfModule>
<IfModule mpm_prefork_module>
</IfModule>

LoadModule dir_module modules/mod_dir.so
LoadModule alias_module modules/mod_alias.so
LoadModule dispatcher_module  modules/mod_dispatcher.so

<IfModule disp_apache2.c>
    DispatcherConfig conf/dispatcher.any
    DispatcherLog    logs/dispatcher.log
    DispatcherLogLevel warn
    DispatcherNoServerHeader Off
    DispatcherDeclineRoot Off
    DispatcherUseProcessedURL Off
    DispatcherPassError 0
</IfModule>

<IfModule unixd_module>
User daemon
Group daemon
</IfModule>

ServerAdmin [email protected]

<Directory />
    AllowOverride none
    Require all denied
</Directory>

DocumentRoot "/usr/local/apache2/htdocs"

<Directory />
    <IfModule disp_apache2.c>
        ModMimeUsePathInfo On
        SetHandler dispatcher-handler
    </IfModule>
    Options FollowSymLinks
    AllowOverride None
</Directory>

<Directory "/usr/local/apache2/htdocs">
    Options Indexes FollowSymLinks
    AllowOverride None
    Require all granted
</Directory>

<IfModule dir_module>
    DirectoryIndex index.html
</IfModule>

<Files ".ht*">
    Require all denied
</Files>

ErrorLog /proc/self/fd/2
LogLevel warn

<IfModule log_config_module>
    LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
    LogFormat "%h %l %u %t \"%r\" %>s %b" common
    <IfModule logio_module>
      LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio
    </IfModule>
    CustomLog /proc/self/fd/1 common
</IfModule>

<IfModule alias_module>
    ScriptAlias /cgi-bin/ "/usr/local/apache2/cgi-bin/"
</IfModule>

<IfModule cgid_module>
</IfModule>

<Directory "/usr/local/apache2/cgi-bin">
    AllowOverride None
    Options None
    Require all granted
</Directory>

<IfModule headers_module>
    RequestHeader unset Proxy early
</IfModule>

<IfModule mime_module>
    TypesConfig conf/mime.types
    AddType application/x-compress .Z
    AddType application/x-gzip .gz .tgz
</IfModule>

<IfModule proxy_html_module>
Include conf/extra/proxy-html.conf
</IfModule>

<IfModule ssl_module>
SSLRandomSeed startup builtin
SSLRandomSeed connect builtin
</IfModule>

Then, add the dispatcher.any file and copy the following lines of code.

/farms
  {  /website
    {
    /clientheaders
      {
      "*"
      }

    /virtualhosts
      {
      "*"
      }

    /renders
      {
      /rend01
        {
        /hostname "aem-publish"
        /port "4503"
        }
      }

    /filter
      {
      /0001 { /type "deny" /glob "*" }

      /0041
        {
        /type "allow"
        /extension '(clientlibs|css|gif|ico|js|png|swf|jpe?g|woff2?)'
        }
      /0081
        {
        /type "deny"
        /selectors '((sys|doc)view|query|[0-9-]+)'
        /extension '(json|xml)'
        }
      /0082
        {
        /type "deny"
        /path "/content"
        /selectors '(feed|rss|pages|languages|blueprint|infinity|tidy)'
        /extension '(json|xml|html)'
        }
      }

    /cache
      {
      /docroot "/usr/local/apache2/htdocs"
      /rules
        {
        /0000
          {
          /glob "*"
          /type "allow"
          }
        }

      /invalidate
        {
        /0000
          {
          /glob "*"
          /type "deny"
          }
        /0001
          {
          /glob "*.html"
          /type "allow"
          }
        /0002
          {
          /glob "/etc/segmentation.segment.js"
          /type "allow"
          }
        /0003
          {
          /glob "*/analytics.sitecatalyst.js"
          /type "allow"
          }
        }

      /allowedClients
        {
        }

      }

    /statistics
      {
      /categories
        {
        /html
          {
          /glob "*.html"
          }
        /others
          {
          /glob "*"
          }
        }
      }
    }
  }

Now, Create the Dockerfile in the same directory and add the following lines of code on in.

# Using the apache base Image
FROM httpd:2.4

# Setting the current working directory
WORKDIR /usr/src/app

# Get the dispacher tar file and keepit inside 'dispacher' folder
ADD https://www.adobeaemcloud.com/content/companies/public/adobe/dispatcher/dispatcher/_jcr_content/top/download_8/file.res/dispatcher-apache2.4-linux-x86-64-4.2.3.tar.gz ./dispatcher/

# Extract the tar file and move it to Apache Modules folder with name mod_dispacher.so
RUN tar -xzvf ./dispatcher/dispatcher-apache2.4-linux-x86-64-4.2.3.tar.gz -C ./dispatcher && \
    cp ./dispatcher/dispatcher-apache2.4-4.2.3.so /usr/local/apache2/modules/mod_dispatcher.so

# Copy the dispacher.any file in to the Image in the below path
COPY dispatcher.any /usr/local/apache2/conf/dispatcher.any

# Copy the apache configuration file in the Image and paste it into the apache configuration path as mentioned below
COPY httpd.conf /usr/local/apache2/conf/httpd.conf

Now, Run the docker command to create the AEM dispatcher image and Container out of it.

$ docker build -t aem-dispatcher .
$ docker run -p 80:80 aem-dispatcher

So, now you will be able to see the dispatcher in http://localhost.

Running all together with Docker Compose.

Sometimes, Starting and Running the AEM Instances one by one is Pain. So, we can have Docker compose file to create the AEM instance altogether. To do so, create the file called docker-compose.yml and put the following content inside it.

version: '3'
services:
  author:
    image: aem-author
    build: ./aem-author
    ports:
      - "4502:4502"
  publish:
    image: aem-publish
    build: ./aem-publish
    ports:
      - "4503:4503"
  dispatcher:
    image: aem-dispatcher
    build: ./aem-dispatcher
    ports:
      - "80:80"
    depends_on:
      - "aem-publish"

Once Added, Just run the following command to create all the Instance together.

$ docker-compose up

Check out this Github to get the entire Source code of this tutorial by clicking here.

Conclusion

Developers are the most affected people in terms of setting up the AEM instance in the local machine with a proper set of repositories and binaries. So, This containerization of AEM instances will help developers to set up the AEM environment in their local, instantly. Also, Enterprise is moving its architectural design from Monolithic to Microservices and in this big digital transformation, this AEM Containerization will be the first step to push forward and hence, learn how to Create AEM instance in Docker with Docker Compose. Stay tuned and subscribe DigitalVarys for more articles and study materials on DevOps, Agile, DevSecOps, and App Development.

2 thoughts on “Create AEM in Docker with Docker Compose.”

  1. This was very helpful for me. Thanks a lot.
    PS. there is an error: for the publish instance Dockerfile, the parameter -Dsling.run.modes is not equal to “publisher” but only to “publish”. Now it works!

  2. what is the actual size of base image after building. because quickstart-crx.jar file itself aroung 500 to 600+MB size. and after building your image showing 5.57mb

Leave a Reply