1. Architecture
The DQE One Server and all its dependencies are packaged as Docker images and run with Docker Compose on a Linux virtual machine. Once your dedicated instance is up and running, you can launch the configuration step from your Salesforce org. The DQE One AppExchange package must be installed beforehand.
This document describes an example of a possible configuration to set up the server instance. It does not cover the security layer of your own environment.
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). 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 DQE One Server only needs to be accessible from Salesforce servers. It is therefore recommended to filter incoming IPs. The exception is the CustomUI interface, which must be accessible from your users' web browsers: allow your company's public IPs or VPN as well. See the list of IPs published by Salesforce (don't forget the Hyperforce IPs).
- Authentication protocol: Salesforce natively offers several protocols to secure requests between its servers and the DQE One Server. When configuring the "DQE One Customer" Connected App in your org, you can select the "Basic Auth" 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, traffic is usually routed through a component such as a gateway or load balancer. In this case, the DNS entry and the SSL certificate are installed on that component, which forwards the traffic to the internal VM.
Recommendation
This section lists the components required to install the DQE One Server instance on a 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 to optimize processing times.
- Operating system: Linux, Ubuntu 22.04 or 24.04 LTS recommended.
- Load balancer: not required, but can be used if already available.
| Component | Minimum | Recommended |
|---|---|---|
| CPU | 4 vCPU | 8 vCPU |
| RAM | 12 GB | 16 GB |
| Disk | 30 GB SSD | 60 GB SSD |
Note: the queue worker alone can use up to 5 GB of memory during processing.
Composition and services
The DQE One Server exposes the services and APIs called by Salesforce through the DQE One app. It is made of the following services, all defined in the docker-compose.yml file described in 2.4.
Web application (web)
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 depends on Redis and RabbitMQ. It is exposed on the web through Nginx only, under the /unify/ path.
-
image: provided by DQE Software and hosted on the DQE Azure Container Registry:
<Name of the registry container>.azurecr.io/<name of the image> - environment: SFAPIVERSION, PORT, REDIS_URL, CLOUDAMQP_URL, CUSTOMUI, UNIFYSERVERURL
- command:
bash ./entrypoint.sh
Worker (worker)
A backend container that processes queued jobs asynchronously. It consumes tasks from the RabbitMQ queue and executes them. It uses the same image as the web application and is not exposed on the web.
- image: same image as the web application
- environment: SFAPIVERSION, WORKDIRPATH, REDIS_URL, CLOUDAMQP_URL
- command:
python -u ./unify/queue_worker.pyc
CustomUI (customui)
A web application exposing the interface used to configure deduplication rules. It must be accessible from your users' web browsers, through Nginx at the root path /. It also calls some backend APIs, for example to retrieve metadata from the CRM.
- image: provided by DQE Software
- environment: PORT, SESSION_SECRET
Redis (redis)
A key/value database used to store internal operating keys such as Salesforce org configurations, sessions, etc.
You can use the official redis image. As a security measure, some cloud providers block public images and only allow images from private registries: in this case, use the compatible Redis image published by DQE on its private registry.
- image:
redis:7.4-alpineor the DQE image - volumes:
redis_data:/data(Docker named volume)
RabbitMQ (rabbitmq)
A queue manager that receives and schedules processing requests. Until a process has been assigned to a worker and completed, it is parked in the queue. This also provides resiliency: if the server is restarted, processing resumes where it left off.
As with Redis, you can use the official image or the one on the DQE private registry.
- image:
rabbitmq:3.13-management-alpineor the DQE image - volumes:
rabbitmq_data:/var/lib/rabbitmq(Docker named volume) - environment: RABBITMQ_DEFAULT_USER, RABBITMQ_DEFAULT_PASS, RABBITMQ_DEFAULT_VHOST
2. Installation
All commands below are run on the VM, with a user that has sudo rights.
2.1. Prerequisites
Docker installation
Install Docker Engine and the Docker Compose plugin from the official Docker repository:
sudo apt-get update
sudo apt-get install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo systemctl enable --now docker
sudo usermod -aG docker $USERLog out and log in again so that your user can run Docker without sudo, then check the installation:
docker --version
docker compose versionDocker Compose
Docker Compose is installed as a plugin in the previous step: use docker compose (with a space). The standalone docker-compose binary is no longer needed.
NGINX installation
sudo apt-get update
sudo apt-get install -y nginx
sudo mkdir -p /etc/nginx/sslCopy your SSL certificate and its private key to /etc/nginx/ssl/, then create the file /etc/nginx/sites-available/dqe-one with the following content:
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/<certificate>.pem;
ssl_certificate_key /etc/nginx/ssl/<private-key>.key;
ssl_protocols TLSv1.2 TLSv1.3;
access_log /var/log/nginx/dqeone.log;
error_log /var/log/nginx/dqeone-error.log error;
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;
# DQE One UI (CustomUI)
location / {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_pass http://127.0.0.1:8001/;
}
# DQE One Server API — the trailing "/" removes the /unify/ prefix
location /unify/ {
proxy_pass http://127.0.0.1:8000/;
}
location /lookup {
proxy_pass http://127.0.0.1:8000;
}
location /static {
proxy_pass http://127.0.0.1:8000;
}
location /API {
proxy_pass http://127.0.0.1:8000;
}
}If TLS is terminated on a gateway or load balancer in front of the VM, keep a single listen 80 server block containing the location sections, without the redirect and the ssl_* lines.
Enable the configuration:
sudo ln -s /etc/nginx/sites-available/dqe-one /etc/nginx/sites-enabled/dqe-one
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t
sudo systemctl reload nginx2.2. Firewall
If the VM firewall (ufw) is used, only open SSH, HTTP and HTTPS:
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enableNote: Docker publishes container ports directly, bypassing ufw. This is why the docker-compose.yml below binds ports 8000 and 8001 to 127.0.0.1: they are only reachable through Nginx.
2.3. Configuration files
Create the application folder and the .env file with the values provided by DQE Software:
sudo mkdir -p /opt/dqe-one
sudo chown $USER /opt/dqe-one
cd /opt/dqe-one
nano .env
nano docker-compose.ymlContent of the .env file. Use strong passwords, without the characters @ : / ? # (the RabbitMQ password is inserted into a connection URL):
# Values provided by DQE Software
SERVER_IMAGE=<registry-url>/<dqe-one-server-image>:<tag>
CUSTOMUI_IMAGE=<registry-url>/<dqe-one-ui-image>:<tag>
REDIS_IMAGE=redis:7.4-alpine
RABBITMQ_IMAGE=rabbitmq:3.13-management-alpine
SFAPIVERSION=v65.0
# Strong passwords, without @ : / ? # (inserted into the AMQP URL)
RABBITMQ_USER=dqeone
RABBITMQ_PASSWORD=ChangeMe_Str0ngPassword
SESSION_SECRET=ChangeMe_LongRandomString2.4. DQE One Server — docker-compose.yml
Content of the docker-compose.yml file:
name: dqe-one
x-logging: &default-logging
driver: json-file
options:
max-size: "50m"
max-file: "5"
services:
customui:
image: ${CUSTOMUI_IMAGE}
container_name: custom-ui
restart: unless-stopped
logging: *default-logging
environment:
- PORT=8001
- SESSION_SECRET=${SESSION_SECRET}
ports:
- "127.0.0.1:8001:8001" # reachable only through Nginx
web:
image: ${SERVER_IMAGE}
container_name: one-server
hostname: oneserver
restart: unless-stopped
logging: *default-logging
depends_on:
redis:
condition: service_healthy
rabbitmq:
condition: service_healthy
environment:
- SFAPIVERSION=${SFAPIVERSION}
- PORT=8000
- REDIS_URL=redis://redis:6379
- CLOUDAMQP_URL=amqp://${RABBITMQ_USER}:${RABBITMQ_PASSWORD}@rabbitmq:5672/admin
- CUSTOMUI=http://customui:8001
- UNIFYSERVERURL=http://web:8000
ports:
- "127.0.0.1:8000:8000" # reachable only through Nginx
command: bash ./entrypoint.sh
worker:
image: ${SERVER_IMAGE}
container_name: queue-worker
restart: unless-stopped
logging: *default-logging
depends_on:
web:
condition: service_started
rabbitmq:
condition: service_healthy
environment:
- SFAPIVERSION=${SFAPIVERSION}
- WORKDIRPATH=/app/unify
- REDIS_URL=redis://redis:6379
- CLOUDAMQP_URL=amqp://${RABBITMQ_USER}:${RABBITMQ_PASSWORD}@rabbitmq:5672/admin
command: python -u ./unify/queue_worker.pyc
redis:
image: ${REDIS_IMAGE}
container_name: redis
hostname: redis
restart: unless-stopped
logging: *default-logging
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 10
rabbitmq:
image: ${RABBITMQ_IMAGE}
container_name: rabbitmq
hostname: rabbitmq
restart: unless-stopped
logging: *default-logging
environment:
- RABBITMQ_DEFAULT_USER=${RABBITMQ_USER}
- RABBITMQ_DEFAULT_PASS=${RABBITMQ_PASSWORD}
- RABBITMQ_DEFAULT_VHOST=admin
volumes:
- rabbitmq_data:/var/lib/rabbitmq
healthcheck:
test: ["CMD", "rabbitmq-diagnostics", "-q", "ping"]
interval: 10s
timeout: 10s
retries: 12
start_period: 30s
volumes:
rabbitmq_data:
redis_data:Key points:
- Passwords and image names are read from the
.envfile. - Redis and RabbitMQ must be healthy before the web application starts, and all containers restart automatically after a VM reboot.
- Logs are rotated (5 × 50 MB per container) so that they cannot fill the disk.
3. Launcher
The DQE Docker images are provided through an Azure Container Registry managed by DQE Software.
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. A successful login displays Login Succeeded.
3.2. Launch of docker-compose.yml
cd /opt/dqe-one
docker compose pull
docker compose up -d
docker compose psExpected output — all services Up, Redis and RabbitMQ healthy:
NAME SERVICE STATUS
custom-ui customui Up
one-server web Up
queue-worker worker Up
rabbitmq rabbitmq Up (healthy)
redis redis Up (healthy)If a container is restarting, check its logs (replace web with the service name):
docker compose logs -f webFinally, open https://<your-domain.com> from a browser to check that the DQE One interface is reachable.
3.3. Update
When DQE Software provides a new version, update the image tags in the .env file, then run:
cd /opt/dqe-one
docker compose pull
docker compose up -d
Related to