1. Architecture
The application and all its dependencies are compiled into Docker images.
As soon as the containers are deployed to a Windows Server instance, the application pairs with a Microsoft Dynamics 365 organization that has the DQE Unify package installed.
This document describes the configuration to deploy DQE Unify Server on a Windows Server 2022 virtual machine using Docker Compose running inside WSL2 (Windows Subsystem for Linux 2).
Flow Diagram
The diagram below describes all the inbound/outbound flows as well as the IPs and Ports requested by the application.
Security Measures
We cannot provide an example of how to implement the security layer that would fit perfectly into your architecture. However, we can provide some recommendations that are relevant to the use of the app itself.
- Protocols and Ports: All outgoing/incoming flows from the application go through the HTTPS protocol (port 443). There is no need to open another port on your VM. The DQE IPs that must be accessible are described in the preceding diagram.
- IP Filtering: The application server itself only needs to be accessible from Microsoft Dynamics 365 servers. It is therefore recommended to set up a filtering of incoming IPs. See the full list of Azure IP ranges at: https://www.microsoft.com/en-us/download/details.aspx?id=56519
- Authentication protocol: When configuring the DQE Unify Connected App in your Dynamics 365 organization, you will be able to select the authentication method and configure your application firewall accordingly.
If the VM is directly exposed to the internet, it must have a DNS entry and an associated SSL certificate that point to it.
If the VM is not directly exposed to the internet, it usually means that traffic is routed through a component such as a gateway or load balancer. In this case, the DNS and the SSL certificate must be installed on that gateway, which will then forward the traffic to the internal VM.
Recommendation
In this section, we describe the list of components required to install the DQE Unify Server instance on a Windows Server VM.
Note: Estimates are based on 1 million records. Depending on the volume of the databases processed, it may be necessary to increase the memory capacity of container instances to optimize processing times.
Deployment with Windows Server VM:
- Server type: Windows Server 2022 (64-bit).
- Container runtime: Docker CE running inside WSL2 (Ubuntu) — required because Docker CE on Windows Server cannot run Linux containers natively.
- Load balancer: Not required, but can be used if already available.
Hardware requirements:
| Component | Minimum | Recommended |
|---|---|---|
| CPU | 4 vCores | 8 vCores |
| RAM | 8 GB | 16 GB |
| Disk | 60 GB SSD | 120 GB SSD |
| Network | 100 Mbit/s | 1 Gbit/s |
Composition and services
The stack is composed of Docker images orchestrated via Docker Compose. Each service runs as a container. Services communicate with each other via their service name on the internal Docker network.
Web Application (unify-server)
A backend container that contains a microservices application. This application exposes all the APIs called to launch processes, manage processing queues, and instantiate processing workers. It is dependent on Redis and RabbitMQ to function properly. This service is exposed on the web through the Nginx reverse proxy.
Docker Compose settings
image: The name of the image hosted on the DQE Azure Container Registry:
<registry-url>/unify-server-web-ms-dynamics:v3.0- depends_on: rabbitmq (healthy), redis (started)
- environment: REDIS_URL, CLOUDAMQP_URL, PORT
- command:
python app.pyc— the image contains compiled Python bytecode, not source files.
Worker (queue-worker)
A backend container that processes queued jobs asynchronously. It consumes tasks from the RabbitMQ queue and executes them. Uses the same Docker image as the web application.
Parameters to set in Docker Compose
- image: Same image as the web application service
- depends_on: rabbitmq (healthy), redis (started)
- environment: REDIS_URL, CLOUDAMQP_URL
- command:
python queue_worker.pyc
Nginx
A reverse proxy that forwards incoming HTTP/HTTPS requests to the Unify Server on port 8000. Its configuration file is stored on the Windows filesystem (C:\dqe-unify\nginxconf\default.conf) and mounted into the container via WSL2 path (/mnt/c/dqe-unify/nginxconf).
- image: nginx:latest
- ports: 80:80, 443:443
- volume:
/mnt/c/dqe-unify/nginxconf:/etc/nginx/conf.d:ro
Redis
A key/value database used to store internal operating keys such as Dynamics 365 org configurations, sessions, etc.
The official redis:alpine image is used. Data is persisted in a Docker named volume stored in the WSL2 Linux filesystem — bind mounts on the Windows NTFS filesystem are not used because they do not support the file permissions required by Redis.
Parameters
- image:
redis:alpine - volumes:
redis-data:/data(Docker named volume)
RabbitMQ
A queue manager allowing the reception and scheduling of processing requests. Until a process has been assigned to a worker and completed, it is parked in the queue. This also provides resiliency to failures — if the server is restarted, it resumes the last processing in the queue where it left off.
The official rabbitmq:3.13-management image is used. Data is persisted in a Docker named volume. Credentials are set via environment variables.
Parameters
- image:
rabbitmq:3.13-management - volumes:
rabbitmq-data:/var/lib/rabbitmq(Docker named volume) - environment: RABBITMQ_DEFAULT_USER=user, RABBITMQ_DEFAULT_PASS=bitnami
2. Installation
All installation steps are performed on the Windows Server VM. Steps marked [PowerShell] must be run in PowerShell as Administrator. Steps marked [WSL2] must be run in the Ubuntu WSL2 terminal.
2.1 Prerequisite
Connect to the Windows Server VM using one of the available connection options (RDP, Azure Bastion, etc.) and open PowerShell as Administrator.
Create the application directory structure from PowerShell:
New-Item -ItemType Directory -Path "C:\dqe-unify"
New-Item -ItemType Directory -Path "C:\dqe-unify\nginxconf"WSL2 installation [PowerShell]
Enable the required Windows features:
dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart
dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart
Enable-WindowsOptionalFeature -Online -FeatureName Containers -All -NoRestart/!\ Reboot required — Restart the VM after enabling these features before continuing.
After reboot, open PowerShell as Administrator and install Ubuntu:
wsl --set-default-version 2
wsl --install -d UbuntuA terminal opens asking you to create a Unix username and password. Complete the setup before continuing.
Docker & Docker Compose installation [WSL2]
Open the Ubuntu WSL2 terminal and run:
curl -fsSL https://get.docker.com | sudo shNote: The script detects WSL and recommends Docker Desktop — ignore the message and wait 20 seconds for the installation to continue automatically. Docker Compose is included in this installation.
Add your user to the docker group and start the service:
sudo usermod -aG docker $USER
sudo service docker startClose and reopen the WSL2 terminal, then verify:
docker --version
docker compose versionNote: The Docker service must be started manually each time the WSL2 session opens: sudo service docker start
Azure CLI installation [WSL2]
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bashNginx configuration [PowerShell]
Nginx runs as a container — there is no host-level installation. You only need to create the configuration file. Open it in Notepad from PowerShell:
notepad C:\dqe-unify\nginxconf\default.confPaste the following content and save:
server {
listen 80;
location / {
proxy_pass http://unify-server:8000;
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_read_timeout 300s;
}
}Note: Unlike the ACI version where Nginx targets localhost:8000, in Docker Compose services communicate via their service name — the proxy target is http://unify-server:8000.
For HTTPS, add a second server block:
server {
listen 443 ssl;
server_name <your-domain.com>;
ssl_certificate /etc/nginx/ssl/cert.crt;
ssl_certificate_key /etc/nginx/ssl/cert.key;
ssl_protocols TLSv1.2 TLSv1.3;
location / {
proxy_pass http://unify-server:8000;
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_read_timeout 300s;
}
}2.2 Windows Unify Server — docker-compose.yml
Create the file C:\dqe-unify\docker-compose.yml with the content below using Notepad. Replace <registry-url> with the URL provided by DQE Software.
Note: Unlike the ACI version, Docker Compose services communicate via service names (redis, rabbitmq), not via localhost. The environment variables are set accordingly.
services:
# ── Redis ──────────────────────────────────────────────
redis:
image: redis:alpine
platform: linux/amd64
restart: always
volumes:
- redis-data:/data
networks:
- unify-net
# ── RabbitMQ ───────────────────────────────────────────
rabbitmq:
image: rabbitmq:3.13-management
platform: linux/amd64
restart: always
ports:
- "5672:5672"
volumes:
- rabbitmq-data:/var/lib/rabbitmq
environment:
- RABBITMQ_DEFAULT_USER=user
- RABBITMQ_DEFAULT_PASS=bitnami
networks:
- unify-net
healthcheck:
test: ["CMD", "rabbitmq-diagnostics", "ping"]
interval: 30s
timeout: 10s
retries: 5
# ── Nginx reverse proxy ────────────────────────────────
nginx:
image: nginx:latest
platform: linux/amd64
restart: always
ports:
- "80:80"
- "443:443"
volumes:
- /mnt/c/dqe-unify/nginxconf:/etc/nginx/conf.d:ro
depends_on:
- unify-server
networks:
- unify-net
# ── Unify Server (web) ─────────────────────────────────
unify-server:
image: <registry-url>/unify-server-web-ms-dynamics:v3.0
restart: always
command: ["python", "app.pyc"]
ports:
- "8000:8000"
environment:
- REDIS_URL=redis://redis:6379
- CLOUDAMQP_URL=amqp://user:bitnami@rabbitmq:5672/
- PORT=8000
depends_on:
rabbitmq:
condition: service_healthy
redis:
condition: service_started
networks:
- unify-net
deploy:
resources:
limits:
memory: 1g
cpus: '0.5'
# ── Queue Worker ───────────────────────────────────────
queue-worker:
image: <registry-url>/unify-server-web-ms-dynamics:v3.0
restart: always
command: ["python", "queue_worker.pyc"]
environment:
- REDIS_URL=redis://redis:6379
- CLOUDAMQP_URL=amqp://user:bitnami@rabbitmq:5672/
depends_on:
rabbitmq:
condition: service_healthy
redis:
condition: service_started
networks:
- unify-net
deploy:
resources:
limits:
memory: 5g
cpus: '1.0'
volumes:
redis-data:
rabbitmq-data:
networks:
unify-net:
driver: bridgeKey points:
redis-dataandrabbitmq-dataare Docker named volumes stored in the WSL2 Linux filesystem. Bind mounts to the Windows NTFS filesystem are not used because NTFS does not support thechownoperations required by these containers.- The Nginx config is mounted from
/mnt/c/dqe-unify/nginxconf— the Windows folder accessible via WSL2. platform: linux/amd64is required on all public images to force Docker (running in WSL2 on a Windows host) to pull the Linux version.- The entry points are
app.pycandqueue_worker.pyc— the DQE image contains compiled Python bytecode, not source.pyfiles.
3. Launcher
The DQE Docker images are provided through an Azure Container Registry managed by DQE Software. All commands in this section are run from the Ubuntu WSL2 terminal.
3.1 Connecting to the DQE Azure Container Registry
Start the Docker service and authenticate to the DQE registry:
sudo service docker start
docker login <registry-url> --username <Login provided by DQE> --password <Pwd provided by DQE>A successful login displays: Login Succeeded
3.2 Launch of docker-compose.yml
Navigate to the application directory and pull the images:
cd /mnt/c/dqe-unify
docker compose pullStart all services in detached mode:
docker compose up -dVerify that all containers are running:
docker compose psExpected output — all services should show status running:
NAME IMAGE STATUS
dqe-unify-nginx-1 nginx:latest running
dqe-unify-rabbitmq-1 rabbitmq:3.13-management running (healthy)
dqe-unify-redis-1 redis:alpine running
dqe-unify-unify-server-1 <registry-url>/unify-server-web-ms-dynamics:v3.0 running
dqe-unify-queue-worker-1 <registry-url>/unify-server-web-ms-dynamics:v3.0 running/!\ Important: If any container shows status exited or restarting, check its logs: docker compose logs <service-name>
Related to