Building a Production-Like AEM Local Development Environment with Docker
A comprehensive guide to isolated Dev and QA environments with AEM Author, Publish, Dispatcher, Nginx, persistent storage, and WKND validation
Local AEM development often begins with a single Author instance. That is sufficient for early coding, but it does not validate the complete delivery path. A mature local setup should make it easy to test Publish behavior, Dispatcher filters and caching, environment-specific configuration, content activation, reverse-proxy routing, and repeatable environment lifecycle management.
|
| PURPOSE This guide presents a reproducible, Docker-based approach to local development and QA validation. It mirrors production engineering practices but does not replace AEM as a Cloud Service environments, Cloud Manager, or Adobe-supported production deployment models. |
What you will build
- Two isolated environments: Development and QA
- AEM Author and Publish services in each environment
- Apache HTTP Server with Dispatcher in front of Publish
- Optional Nginx reverse proxy for friendly local domains
- Persistent volumes for repositories and logs
- Environment-specific run modes and configuration
- WKND-based end-to-end validation
- Operational commands, troubleshooting guidance, and security guardrails
Who this guide is for
AEM developers, technical leads, architects, DevOps engineers, and team members responsible for onboarding or standardizing local development environments.
Guide map
| 01 | Why Docker for AEM |
| 02 | Reference architecture |
| 03 | Prerequisites and capacity planning |
| 04 | Repository and configuration model |
| 05 | Docker Compose foundation |
| 06 | Dispatcher and Nginx |
| 07 | WKND validation workflow |
| 08 | Daily operations |
| 09 | Troubleshooting playbook |
| 10 | Security, limitations, and next steps |
01 Why Docker for AEM development?
Docker does not remove the complexity of AEM. It makes that complexity explicit, versionable, and repeatable. The value comes from codifying service relationships, configuration, ports, networks, storage, and startup behavior so that the wider team can reproduce the same environment.
Consistency
Developers use the same service topology and configuration model, reducing machine-specific differences.
Isolation
Dev and QA repositories, caches, logs, and networks remain separate.
Repeatability
Compose commands provide a predictable lifecycle for start, stop, rebuild, and reset.
Portability
The environment definition can be version-controlled alongside project documentation.
Earlier validation
Developers can test the Browser → Proxy → Dispatcher → Publish route before promoting changes.
|
| KEY IDEA Do not optimize for the maximum number of containers on day one. Begin with one Author, one Publish, and the official local Dispatcher runtime. Add a second isolated environment after the first path is stable. |
02 Reference architecture
The reference topology uses two independent Compose projects. Each project has its own network, named volumes, ports, service names, and environment file.
| AEM Author | AEM Publish | Dispatcher | Nginx |
| Content & admin | Delivery runtime | Cache & filter | Local front door |
DEVELOPMENT • HOST PORTS 4502 / 4503 / 8080 / 80
| AEM Author | AEM Publish | Dispatcher | Nginx |
| Content & admin | Delivery runtime | Cache & filter | Local front door |
QA • HOST PORTS 4512 / 4513 / 8081 / 81
Request path
| Browser → Nginx → Apache Dispatcher → AEM Publish |
Author remains available for authoring, package management, administration, and activation. Publish receives activated content and serves delivery requests through Dispatcher.
Port plan
| Service | Development | QA |
| AEM Author | 4502 | 4512 |
| AEM Publish | 4503 | 4513 |
| Dispatcher | 8080 | 8081 |
| Nginx | 80 | 81 |
| Author debugger | 30303 | 30313 |
| Publish debugger | 30304 | 30314 |
03 Prerequisites and capacity planning
Required software and artifacts
- A supported Java installation matched to the AEM SDK version used by the project.
- The AEM as a Cloud Service SDK, including the local Quickstart JAR and compatible Dispatcher Tools.
- Docker Desktop or Docker Engine with the Docker Compose plugin.
- A Git client and sufficient permissions to update the local hosts file.
- A compatible WKND release, if reference content is required for validation.
|
| LICENSE AND DISTRIBUTION Do not commit AEM Quickstart JARs, licensed SDK assets, credentials, private keys, or proprietary content packages to source control or publish them in public container registries. |
Suggested workstation baseline
| Resource | Starting point | Recommended for two environments |
| CPU | 4 cores | 6–8 cores when running both environments |
| Memory | 8 GB available | 16 GB or more preferred |
| Storage | SSD with free capacity | 60 GB or more for images, volumes, and logs |
Treat these figures as planning guidance rather than fixed product requirements. Measure actual utilization for your SDK, project codebase, package set, and host operating system.
04 Repository and configuration model
Recommended repository structure
| aem-local/ ├── compose.yaml ├── compose.dev.yaml ├── compose.qa.yaml ├── .env.dev ├── .env.qa ├── aem/ │ ├── author/ │ └── publish/ ├── dispatcher/ │ └── src/ ├── nginx/ │ └── conf.d/ ├── packages/ └── scripts/ |
Environment-specific settings
| COMPOSE_PROJECT_NAME=aem-dev AUTHOR_PORT=4502 PUBLISH_PORT=4503 DISPATCHER_PORT=8080 NGINX_PORT=80 AUTHOR_RUNMODES=author,dev PUBLISH_RUNMODES=publish,dev DOMAIN_NAME=local-dev.aemaacs.com |
Store non-sensitive differences in environment files. Keep secrets in untracked local files or an approved secret-management mechanism.
Run-mode configuration
| config.author.dev config.publish.dev config.author.qa config.publish.qa |
|
| DESIGN RULE Use run modes only for configuration that genuinely differs by tier or environment. Avoid duplicating configuration when the same value should be shared. |
05 Docker Compose foundation
The following excerpt illustrates service relationships and storage. Adapt image-building details, users, permissions, health checks, and startup commands to your organization’s approved local-development pattern.
| services: author: build: ./aem/author ports: - "${AUTHOR_PORT}:4502" volumes: - author-data:/opt/aem/crx-quickstart/repository - author-logs:/opt/aem/crx-quickstart/logs networks: [aem-network]
publish: build: ./aem/publish ports: - "${PUBLISH_PORT}:4503" volumes: - publish-data:/opt/aem/crx-quickstart/repository - publish-logs:/opt/aem/crx-quickstart/logs networks: [aem-network]
dispatcher: build: ./dispatcher depends_on: [publish] ports: - "${DISPATCHER_PORT}:8080" networks: [aem-network]
nginx: image: nginx:stable depends_on: [dispatcher] ports: - "${NGINX_PORT}:80" networks: [aem-network]
volumes: author-data: author-logs: publish-data: publish-logs:
networks: aem-network: |
Persistence strategy
- Use named volumes for repository data and logs.
- Keep Dev and QA volume names independent.
- Use bind mounts primarily for source-controlled configuration or package input.
- Document the reset behavior clearly because deleting volumes destroys local repository data.
06 Dispatcher and Nginx
Use compatible Dispatcher Tools
Build the local Dispatcher from the tools delivered with the AEM SDK and validate configuration using the SDK utilities. This helps align the local Dispatcher configuration with supported cloud deployment patterns.
Filter strategy
Adopt a deny-by-default model and allow only the methods and URL patterns required by the application. The following is conceptual only and must not be copied as a complete production security policy:
| /0001 { /type "allow" /method "GET" /url "/content/*" } /0002 { /type "allow" /method "GET" /url "/etc.clientlibs/*" } /9999 { /type "deny" /url "*" } |
|
| SECURITY CHECK Review administrative, diagnostic, repository, authoring, internal, and custom API endpoints. A broad allow rule can unintentionally expose sensitive functionality. |
Caching considerations
- Authentication and authorization
- Personalized or cookie-dependent responses
- Query-parameter handling
- Cache invalidation and stat-file behavior
- Vanity URLs, redirects, error pages, and client libraries
- GraphQL and other API responses
Nginx reverse proxy
| server { listen 80; server_name local-dev.aemaacs.com;
location / { proxy_pass http://dispatcher:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } } |
Add the domain names to the local hosts file:
| 127.0.0.1 local-dev.aemaacs.com 127.0.0.1 local-qa.aemaacs.com |
07 WKND validation workflow
WKND provides reference content and application behavior that can be used to validate the complete local delivery chain. Choose a release compatible with the project’s AEM SDK and Java version.
- Deploy the compatible WKND package to Author.
- Confirm the site renders on Author.
- Configure and test local activation or replication to Publish.
- Publish the required site content and dependencies.
- Verify the page directly on Publish.
- Verify the same page through Dispatcher.
- Verify the page through the Nginx domain.
- Repeat an eligible request and inspect whether Dispatcher serves it from cache.
|
| SUCCESS CRITERIA Complete the test only when content is visible through the full path and expected security and caching behavior can be demonstrated. |
08 Daily operations
Start Development
| docker compose --env-file .env.dev -f compose.yaml -f compose.dev.yaml up -d |
Start QA
| docker compose --env-file .env.qa -f compose.yaml -f compose.qa.yaml up -d |
Inspect status and logs
| docker compose ps docker compose logs -f docker compose logs -f author docker stats |
Stop or reset
| # Preserve named volumes docker compose down
# Destructive reset: remove named volumes docker compose down --volumes |
09 Troubleshooting playbook
| Symptom | What to inspect |
| AEM does not become ready | Check Java/SDK compatibility, memory, disk, permissions, repository health, and container logs. |
| Host port is in use | Identify the owning process or change the host-side port in the environment file. |
| Dispatcher fails to start | Run the validator supplied with the matching Dispatcher Tools package and inspect Apache logs. |
| Content is not cached | Review filters, cache rules, response headers, cookies, query handling, permissions, and invalidation. |
| Local domain does not resolve | Check the hosts file, proxy port mapping, Nginx configuration, and forwarded Host header. |
| Dev communicates with QA | Inspect network membership and ensure services are attached only to the intended project network. |
Useful port checks
| # macOS / Linux lsof -i :4502
# Windows PowerShell / Command Prompt netstat -ano | findstr :4502 |
10 Security, limitations, and next steps
Security checklist
- Keep SDK artifacts, credentials, and keys out of Git.
- Do not publish images containing licensed AEM binaries to public registries.
- Bind local-only services to loopback where appropriate.
- Use explicit Dispatcher allowlists and test denied paths.
- Use read-only mounts for configuration inputs when possible.
- Maintain base images and scan them according to organizational policy.
- Back up important named volumes before destructive reset operations.
Understand the boundary
A local SDK environment supports rapid development and validation, but it does not reproduce every service, operational control, deployment behavior, or managed capability available in AEM as a Cloud Service. Use Cloud Manager and appropriate Adobe environments for cloud deployment validation.
Recommended enhancements
- Health checks and readiness gates
- Local HTTPS and certificate handling
- Automated Dispatcher validation in CI
- Smoke tests for Author, Publish, Dispatcher, and proxy routes
- Centralized logs or lightweight local observability
- Backup and restore scripts for named volumes
- Documented image-version and SDK-version compatibility matrix
Conclusion
A successful Docker-based AEM environment is more than a containerized Quickstart JAR. It is a reproducible engineering platform that codifies the complete local request path, separates environment data, validates Dispatcher behavior early, and gives every developer a consistent operating model.
Start small, validate the full path, and expand deliberately. Once the first Author, Publish, and Dispatcher stack is reliable, add QA isolation, custom domains, automated checks, and observability. The result is a practical foundation for development, troubleshooting, demonstrations, training, and team onboarding.
|
| COMMUNITY QUESTION How is your team standardizing local AEM environments? Share your approach to Author, Publish, Dispatcher, containers, and environment isolation. |
References
Local Development Environment for AEM as a Cloud Service
Adobe Experience League • https://experienceleague.adobe.com/en/docs/experience-manager-learn/cloud-service/local-development-environment-set-up/overview
Set up local AEM SDK
Adobe Experience League • https://experienceleague.adobe.com/en/docs/experience-manager-learn/cloud-service/local-development-environment-set-up/aem-runtime
Set up local Dispatcher Tools
Adobe Experience League • https://experienceleague.adobe.com/en/docs/experience-manager-learn/cloud-service/local-development-environment-set-up/dispatcher-tools
Dispatcher in the Cloud
Adobe Experience League • https://experienceleague.adobe.com/en/docs/experience-manager-cloud-service/content/implementing/content-delivery/disp-overview
AEM as a Cloud Service SDK
Adobe Experience League • https://experienceleague.adobe.com/en/docs/experience-manager-cloud-service/content/implementing/developing/aem-as-a-cloud-service-sdk
Adobe WKND releases
GitHub • https://github.com/adobe/aem-guides-wknd/releases