VM Windows — Salesforce installation

Support DQE
Support DQE
  • Updated

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 Salesforce org that has the DQE One package installed.

This document describes the configuration to deploy DQE One Server on a Windows Server 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.

FlowMatrix

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). Port 80 is only used to redirect to HTTPS. 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 Salesforce servers. It is therefore recommended to set up a filtering of incoming IPs. The Salesforce IP ranges are listed in the Salesforce Help article Salesforce IP Addresses and Domains to Allow.
  • Authentication protocol: When configuring the DQE One app in your Salesforce org, 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 One 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 2025 (64-bit), recommended. Windows Server 2022 is supported but requires an additional port forwarding step (see 2.4).
  • Container runtime: Docker Engine running inside WSL2 (Ubuntu) — required because Docker on Windows Server can only run Windows containers, and the DQE One images are Linux images.
  • Azure VM: choose a size that supports nested virtualization (e.g. Dsv5 / Esv5 series), which WSL2 requires.
  • Load balancer: Not required, but can be used if already available.

Hardware requirements:

ComponentMinimumRecommended
CPU4 vCores8 vCores
RAM16 GB32 GB
Disk60 GB SSD120 GB SSD
Network100 Mbit/s1 Gbit/s

Note: 16 GB is the minimum because the queue worker alone can use up to 5 GB, and WSL2 must be given enough memory to run the whole stack (see 2.2).

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 (one-server)

A backend container that contains a microservices application. This application exposes all the APIs called by Salesforce 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 only.

Docker Compose settings

  • image: The name of the image hosted on the DQE Azure Container Registry:

    <registry-url>/<dqe-one-server-image>:<tag>
  • depends_on: redis (healthy), rabbitmq (healthy), one-ui (started)
  • environment: SFAPIVERSION, PORT, REDIS_URL, CLOUDAMQP_URL, CUSTOMUI, UNIFYSERVERURL
  • command: bash ./entrypoint.sh

Worker (queue-worker)

A backend container that processes queued jobs asynchronously. It consumes tasks from the RabbitMQ queue and executes them.

Parameters to set in Docker Compose

  • image: provided by DQE Software
  • depends_on: redis (healthy), rabbitmq (healthy), one-server (started)
  • environment: SFAPIVERSION, WORKDIRPATH, REDIS_URL, CLOUDAMQP_URL
  • command: python -u ./unify/queue_worker.pyc — the image contains compiled Python bytecode, not source files.

User Interface (one-ui)

The DQE One user interface, called by the web application. It is only reachable inside the Docker network.

Parameters to set in Docker Compose

  • image: provided by DQE Software
  • environment: PORT, SESSION_SECRET
  • command: npm start

Nginx

A reverse proxy that terminates HTTPS and forwards incoming requests to the DQE One Server on port 8000. Its configuration file and SSL certificate are stored on the Windows filesystem (C:\dqe-one\nginxconf and C:\dqe-one\ssl) and mounted into the container via WSL2 paths.

  • image: nginx:1.28-alpine
  • ports: 80:80, 443:443
  • volumes: /mnt/c/dqe-one/nginxconf:/etc/nginx/conf.d:ro, /mnt/c/dqe-one/ssl:/etc/nginx/ssl:ro

Redis

A key/value database used to store internal operating keys such as Salesforce org configurations, sessions, etc.

The official redis 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:7.4-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 in the .env file. RabbitMQ is not exposed outside the VM; its management UI is reachable from the VM only (http://localhost:15672).

Parameters

  • image: rabbitmq:3.13-management
  • volumes: rabbitmq-data:/var/lib/rabbitmq (Docker named volume)
  • environment: RABBITMQ_DEFAULT_USER, RABBITMQ_DEFAULT_PASS, RABBITMQ_DEFAULT_VHOST=admin

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-one\nginxconf", "C:\dqe-one\ssl" -Force

2.2 WSL2 installation [PowerShell]

wsl --install --no-distribution
wsl --update

/!\ Reboot required — Restart the VM before continuing.

After reboot, open PowerShell as Administrator and install Ubuntu:

wsl --install -d Ubuntu-24.04

A terminal opens asking you to create a Unix username and password. Complete the setup before continuing.

Then create the WSL2 configuration file (back in PowerShell as Administrator):

@"
[wsl2]
networkingMode=mirrored
memory=12GB
processors=4
"@ | Set-Content -Path "$env:USERPROFILE\.wslconfig" -Encoding ascii
  • networkingMode=mirrored exposes the container ports directly on the VM IP address (Windows Server 2025). Without it, ports published in WSL2 are only reachable from the VM itself.
  • memory: by default WSL2 only uses 50% of the VM memory. Adjust to the VM size, never below 10 GB.

2.3 Docker & Docker Compose installation [WSL2]

Open the Ubuntu WSL2 terminal and enable systemd so that Docker starts automatically with WSL2:

printf '[boot]\nsystemd=true\n' | sudo tee /etc/wsl.conf

From PowerShell, restart WSL2 with wsl --shutdown, then reopen the Ubuntu WSL2 terminal and install Docker:

curl -fsSL https://get.docker.com | sudo sh

Note: 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 enable the service:

sudo usermod -aG docker $USER
sudo systemctl enable --now docker

Close and reopen the WSL2 terminal, then verify:

docker --version
docker compose version

2.4 Network and firewall [PowerShell]

New-NetFirewallRule -DisplayName "DQE One HTTP"  -Direction Inbound -Protocol TCP -LocalPort 80  -Action Allow
New-NetFirewallRule -DisplayName "DQE One HTTPS" -Direction Inbound -Protocol TCP -LocalPort 443 -Action Allow
Set-NetFirewallHyperVVMSetting -Name '{40E0AC32-46A5-438A-A0B2-2B479E8F2E90}' -DefaultInboundAction Allow

The last command allows inbound traffic to WSL2 through the Hyper-V firewall, which is required in mirrored mode.

Windows Server 2022 only: mirrored mode is not available. Forward ports 80 and 443 to the WSL2 IP address. This IP changes at each restart, so the commands must also run at startup:

$ip = (wsl -d Ubuntu-24.04 hostname -I).Split(' ')[0]
foreach ($p in 80,443) {
  netsh interface portproxy delete v4tov4 listenport=$p listenaddress=0.0.0.0
  netsh interface portproxy add v4tov4 listenport=$p listenaddress=0.0.0.0 connectport=$p connectaddress=$ip
}

2.5 Automatic start at boot [PowerShell]

WSL2 does not start with Windows and stops when no Windows process uses it. The scheduled task below keeps it running from boot, without an open session. Docker then starts through systemd and the containers restart automatically.

$cred     = Get-Credential -UserName "$env:USERDOMAIN\$env:USERNAME" -Message "Account that runs WSL2 at startup"
$action   = New-ScheduledTaskAction -Execute "wsl.exe" -Argument "-d Ubuntu-24.04 -u root -- sleep infinity"
$trigger  = New-ScheduledTaskTrigger -AtStartup
$settings = New-ScheduledTaskSettingsSet -ExecutionTimeLimit ([TimeSpan]::Zero) -RestartCount 3 -RestartInterval (New-TimeSpan -Minutes 1)
Register-ScheduledTask -TaskName "DQE-One-WSL-KeepAlive" -Action $action -Trigger $trigger -Settings $settings `
  -User $cred.UserName -Password $cred.GetNetworkCredential().Password -RunLevel Highest
Start-ScheduledTask -TaskName "DQE-One-WSL-KeepAlive"

Note: use the same account that installed Ubuntu — WSL2 distributions are installed per user.

2.6 Nginx configuration [PowerShell]

Nginx runs as a container — there is no host-level installation. You only need to copy the SSL certificate and create the configuration file.

  1. Copy the certificate and its private key to C:\dqe-one\ssl\cert.crt and C:\dqe-one\ssl\cert.key.
  2. Open the configuration file in Notepad: notepad C:\dqe-one\nginxconf\default.conf

Paste the following content and save:

server {
    listen 80;
    server_name <your-domain.com>;
    return 301 https://$host$request_uri;
}

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://one-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_set_header   X-Forwarded-Proto $scheme;
        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://one-server:8000.

If SSL is terminated on a gateway in front of the VM, keep a single listen 80 block containing the location / section.

2.7 Windows DQE One Server — .env and docker-compose.yml

Create the file C:\dqe-one\.env with the values provided by DQE Software. Use strong passwords without the characters @ : / ? # (the RabbitMQ password is inserted into a connection URL).

# Copy to C:\dqe-one\.env and fill in
DQE_ONE_SERVER_IMAGE=<registry-url>/<dqe-one-server-image>:<tag>
DQE_ONE_WORKER_IMAGE=<registry-url>/<dqe-one-server-image>:<tag>
DQE_ONE_UI_IMAGE=<registry-url>/<dqe-one-ui-image>:<tag>

SFAPIVERSION=v65.0

# Avoid @ : / ? # (the password is injected into the AMQP URL)
RABBITMQ_USER=dqeone
RABBITMQ_PASSWORD=ChangeMe_Str0ngPassword

SESSION_SECRET=ChangeMe_LongRandomString

Create the file C:\dqe-one\docker-compose.yml with the content below using Notepad.

Note: Unlike the ACI version, Docker Compose services communicate via service names (redis, rabbitmq, one-ui), not via localhost. The environment variables are set accordingly.

# =============================================================================
#  DQE One for Salesforce — Windows Server VM (Docker Engine in WSL2)
#  From WSL2:  cd /mnt/c/dqe-one && docker compose up -d
# =============================================================================
name: dqe-one

x-logging: &default-logging          # log rotation (json-file grows without limit otherwise)
  driver: json-file
  options:
    max-size: "50m"
    max-file: "5"

services:

  # ── Redis ────────────────────────────────────────────────────────────────────
  redis:
    image: redis:7.4-alpine
    restart: always
    logging: *default-logging
    volumes:
      - redis-data:/data
    networks:
      - dqe-one-net
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 10

  # ── RabbitMQ ─────────────────────────────────────────────────────────────────
  rabbitmq:
    image: rabbitmq:3.13-management
    hostname: rabbitmq                # stable node name => queues kept across re-creations
    restart: always
    logging: *default-logging
    ports:
      - "127.0.0.1:15672:15672"       # management UI, reachable from the VM only
    volumes:
      - rabbitmq-data:/var/lib/rabbitmq
    environment:
      - RABBITMQ_DEFAULT_USER=${RABBITMQ_USER}
      - RABBITMQ_DEFAULT_PASS=${RABBITMQ_PASSWORD}
      - RABBITMQ_DEFAULT_VHOST=admin
    networks:
      - dqe-one-net
    healthcheck:
      test: ["CMD", "rabbitmq-diagnostics", "-q", "ping"]
      interval: 10s
      timeout: 10s
      retries: 12
      start_period: 30s

  # ── Nginx reverse proxy ──────────────────────────────────────────────────────
  nginx:
    image: nginx:1.28-alpine
    restart: always
    logging: *default-logging
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /mnt/c/dqe-one/nginxconf:/etc/nginx/conf.d:ro
      - /mnt/c/dqe-one/ssl:/etc/nginx/ssl:ro
    depends_on:
      one-server:
        condition: service_started
    networks:
      - dqe-one-net

  # ── DQE One UI ───────────────────────────────────────────────────────────────
  one-ui:
    image: ${DQE_ONE_UI_IMAGE}
    restart: always
    logging: *default-logging
    command: ["npm", "start"]
    environment:
      - PORT=8001
      - SESSION_SECRET=${SESSION_SECRET}
    networks:
      - dqe-one-net

  # ── DQE One Server (web) ─────────────────────────────────────────────────────
  one-server:
    image: ${DQE_ONE_SERVER_IMAGE}
    hostname: oneserver
    restart: always
    logging: *default-logging
    command: ["bash", "./entrypoint.sh"]
    ports:
      - "127.0.0.1:8000:8000"         # local debugging only — public traffic goes through Nginx
    environment:
      - SFAPIVERSION=${SFAPIVERSION}
      - PORT=8000
      - REDIS_URL=redis://redis:6379
      - CLOUDAMQP_URL=amqp://${RABBITMQ_USER}:${RABBITMQ_PASSWORD}@rabbitmq:5672/admin
      - CUSTOMUI=http://one-ui:8001
      - UNIFYSERVERURL=http://one-server:8000
    depends_on:
      redis:
        condition: service_healthy
      rabbitmq:
        condition: service_healthy
      one-ui:
        condition: service_started
    networks:
      - dqe-one-net
    deploy:
      resources:
        limits:
          memory: 2g
          cpus: '1.0'

  # ── Queue Worker ─────────────────────────────────────────────────────────────
  queue-worker:
    image: ${DQE_ONE_WORKER_IMAGE}
    restart: always
    logging: *default-logging
    command: ["python", "-u", "./unify/queue_worker.pyc"]
    environment:
      - SFAPIVERSION=${SFAPIVERSION}
      - WORKDIRPATH=/app/unify
      - REDIS_URL=redis://redis:6379
      - CLOUDAMQP_URL=amqp://${RABBITMQ_USER}:${RABBITMQ_PASSWORD}@rabbitmq:5672/admin
    depends_on:
      redis:
        condition: service_healthy
      rabbitmq:
        condition: service_healthy
      one-server:
        condition: service_started
    networks:
      - dqe-one-net
    deploy:
      resources:
        limits:
          memory: 5g
          cpus: '1.0'

volumes:
  redis-data:
  rabbitmq-data:

networks:
  dqe-one-net:
    driver: bridge

Key points:

  • Only Nginx is exposed on the network. Redis, RabbitMQ and the UI are only reachable inside the Docker network; one-server and the RabbitMQ management UI are bound to 127.0.0.1 for troubleshooting from the VM.
  • redis-data and rabbitmq-data are Docker named volumes stored in the WSL2 Linux filesystem. Bind mounts to the Windows NTFS filesystem are not used because NTFS does not support the chown operations required by these containers.
  • Services start in order: Redis and RabbitMQ must be healthy before one-server and queue-worker start.
  • Logs are rotated (5 × 50 MB per container) so that they cannot fill the disk.
  • Public image versions are pinned so that a restart never pulls an unexpected new version.

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

docker login <registry-url> --username <Login provided by DQE>

Enter the password provided by DQE when prompted (it is not kept in the shell history). 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-one
docker compose pull

Start all services in detached mode:

docker compose up -d

Verify that all containers are running:

docker compose ps

Expected output — all services should show status running, and Redis and RabbitMQ healthy:

NAME                      SERVICE        STATUS
dqe-one-nginx-1           nginx          running
dqe-one-one-server-1      one-server     running
dqe-one-one-ui-1          one-ui         running
dqe-one-queue-worker-1    queue-worker   running
dqe-one-rabbitmq-1        rabbitmq       running (healthy)
dqe-one-redis-1           redis          running (healthy)

/!\ Important: If any container shows status exited or restarting, check its logs: docker compose logs <service-name>

3.3 Verification

From a machine outside the VM, open https://<your-domain.com>. Then restart the VM once and run docker compose ps to check that all containers came back automatically.

3.4 Update

When DQE Software provides a new version, update the image tags in C:\dqe-one\.env, then run:

cd /mnt/c/dqe-one
docker compose pull
docker compose up -d

Related to

Was this article helpful?

0 out of 0 found this helpful