Overview
When integrating the Virtru Gateway with Microsoft 365 or Google Workspace, it’s often necessary to run multiple containers to handle distinct encryption flows (e.g., inbound vs. outbound). Since Microsoft 365 doesn’t natively support multiple connectors within a single containerized environment, the best solution is to assign unique external IP addresses and/or custom ports to each Virtru container.
This guide walks through assigning unique internal IP addresses to multiple Docker-based Virtru Gateway containers on a single GCP VM, allowing each container to listen on port 25 without conflicts.
Note: IP addresses shown are examples. Use your actual assigned IPs. With each container bound to a unique IP, you can reuse the same port (e.g., 25) across all containers without conflict. Alternatively, you can assign unique external ports (e.g., 9001, 9002) that map to port 25 inside the container.
Important: This setup requires multiple Network Interface Cards (NICs) on your VM. Please coordinate with your network team to allocate external IPs or ports. Virtru Support can assist with updating application scripts as needed.
You can follow your cloud provider’s guide to add additional NICs:
GCP: https://cloud.google.com/vpc/docs/configure-routing-additional-interface
AWS: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/create-network-interface.html
Azure: https://learn.microsoft.com/en-us/azure/virtual-network/virtual-network-network-interface-vm
Prerequisites
- A running GCP VM with Docker installed
- Virtru Gateway image pulled (
containers.virtru.com/gateway:v<version>) - Access to the GCP Console (Compute Engine → VM Instances)
- Separate
.envfiles per container - Sudo/root access on the VM
Architecture Summary
Find Your Primary IP
ip -4 addr show ens4 | grep "inet " | awk '{print $2}'This will output something like:
10.128.0.83/32
Your primary IP is 10.128.0.83 — that's the one GCP assigned to the VM.
Determine Available Alias IPs
Since GCP assigned your VM 10.128.0.83 within the subnet 10.128.0.0/20, run this to see what's in use across the whole interface:
ip -4 addr show | grep inet
Then ping a range to find free IPs:
for ip in $(seq 80 95); do ping -c 1 -W 1 10.128.0.$ip > /dev/null 2>&1 \ && echo "10.128.0.$ip - IN USE" \ || echo "10.128.0.$ip - available" done
.83) can be reused for one container.| Container | Role | Internal IP | Port |
|---|---|---|---|
od-25 |
Outbound Decrypt | 10.128.0.83 |
25 |
dlp-out-25 |
Outbound DLP | 10.128.0.84 |
25 |
oe-25 |
Outbound Encrypt | 10.128.0.85 |
25 |
ie-25 |
Inbound Encrypt | 10.128.0.86 |
25 |
id-25 |
Inbound Decrypt | 10.128.0.87 |
25 |
Step 1: Add Alias IPs in GCP Console
- Navigate to Compute Engine → VM Instances
- Click your instance → Edit
- Scroll to Network Interface → Alias IP Ranges
- Click "+ Add IP range" and add one entry per additional container:
| Subnet Range | Alias IP Range |
|---|---|
| Primary (10.128.0.0/20) | 10.128.0.84/32 |
| Primary (10.128.0.0/20) | 10.128.0.85/32 |
| Primary (10.128.0.0/20) | 10.128.0.86/32 |
| Primary (10.128.0.0/20) | 10.128.0.87/32 |
- Click Save
.83) is already assigned — alias IPs start at .84
Step 2: Verify IPs Are Available
Before assigning, confirm the IPs are not already in use:
for ip in 10.128.0.84 10.128.0.85 10.128.0.86 10.128.0.87; do ping -c 1 -W 1 $ip /dev/null 2&1 && echo "$ip IN USE" || echo "$ip available" done
Step 3: Assign Alias IPs to the Network Interface
sudo ip addr add 10.128.0.84/32 dev ens4 sudo ip addr add 10.128.0.85/32 dev ens4 sudo ip addr add 10.128.0.86/32 dev ens4 sudo ip addr add 10.128.0.87/32 dev ens4
Verify:
ip a show ens4
Expected output should show all 5 IPs (.83 through .87) bound to ens4.
Make IPs Persistent Across Reboots
Create a systemd service so alias IPs survive reboots:
sudo tee /etc/systemd/system/alias-ips.service /dev/null <<EOF [Unit] Description=Add Alias IPs to ens4 After=network.target [Service] Type=oneshot ExecStart=/sbin/ip addr add 10.128.0.84/32 dev ens4 ExecStart=/sbin/ip addr add 10.128.0.85/32 dev ens4 ExecStart=/sbin/ip addr add 10.128.0.86/32 dev ens4 ExecStart=/sbin/ip addr add 10.128.0.87/32 dev ens4 RemainAfterExit=yes [Install] WantedBy=multi-user.target EOF sudo systemctl daemon-reload sudo systemctl enable alias-ips.service
Step 4: Create Queue Directories
mkdir -p /var/virtru/vg/queue/od-25 mkdir -p /var/virtru/vg/queue/dlp-out-25 mkdir -p /var/virtru/vg/queue/oe-25 mkdir -p /var/virtru/vg/queue/ie-25 mkdir -p /var/virtru/vg/queue/id-25
Step 5: Create Setup Scripts
Navigate to your scripts directory:
cd /var/virtru/vg/scripts
setup-od-25.sh
docker run \ --env-file /var/virtru/vg/env/od-25.env \ -v /var/virtru/vg/tls/:/etc/postfix/tls \ -v /var/virtru/vg/queue/od-25/:/var/spool/postfix \ -v /var/virtru/vg/dkim/:/etc/opendkim/keys \ --name od-25 \ --publish 10.128.0.83:25:25 \ --interactive --tty --detach \ --restart unless-stopped \ --log-driver json-file \ --log-opt max-size=10m \ --log-opt max-file=100 \ containers.virtru.com/gateway:v2.69.0
setup-dlp-out-25.sh
docker run \ --env-file /var/virtru/vg/env/dlp-out-25.env \ -v /var/virtru/vg/tls/:/etc/postfix/tls \ -v /var/virtru/vg/queue/dlp-out-25/:/var/spool/postfix \ -v /var/virtru/vg/dkim/:/etc/opendkim/keys \ --name dlp-out-25 \ --publish 10.128.0.84:25:25 \ --interactive --tty --detach \ --restart unless-stopped \ --log-driver json-file \ --log-opt max-size=10m \ --log-opt max-file=100 \ containers.virtru.com/gateway:v2.69.0
setup-oe-25.sh
docker run \ --env-file /var/virtru/vg/env/oe-25.env \ -v /var/virtru/vg/tls/:/etc/postfix/tls \ -v /var/virtru/vg/queue/oe-25/:/var/spool/postfix \ -v /var/virtru/vg/dkim/:/etc/opendkim/keys \ --name oe-25 \ --publish 10.128.0.85:25:25 \ --interactive --tty --detach \ --restart unless-stopped \ --log-driver json-file \ --log-opt max-size=10m \ --log-opt max-file=100 \ containers.virtru.com/gateway:v2.69.0
setup-ie-25.sh
docker run \ --env-file /var/virtru/vg/env/ie-25.env \ -v /var/virtru/vg/tls/:/etc/postfix/tls \ -v /var/virtru/vg/queue/ie-25/:/var/spool/postfix \ -v /var/virtru/vg/dkim/:/etc/opendkim/keys \ --name ie-25 \ --publish 10.128.0.86:25:25 \ --interactive --tty --detach \ --restart unless-stopped \ --log-driver json-file \ --log-opt max-size=10m \ --log-opt max-file=100 \ containers.virtru.com/gateway:v2.69.0
setup-id-25.sh
docker run \ --env-file /var/virtru/vg/env/id-25.env \ -v /var/virtru/vg/tls/:/etc/postfix/tls \ -v /var/virtru/vg/queue/id-25/:/var/spool/postfix \ -v /var/virtru/vg/dkim/:/etc/opendkim/keys \ --name id-25 \ --publish 10.128.0.87:25:25 \ --interactive --tty --detach \ --restart unless-stopped \ --log-driver json-file \ --log-opt max-size=10m \ --log-opt max-file=100 \ containers.virtru.com/gateway:v2.69.0
Make all scripts executable:
chmod +x /var/virtru/vg/scripts/setup-*.sh
Step 6: Launch All Containers
bash setup-od-25.sh bash setup-dlp-out-25.sh bash setup-oe-25.sh bash setup-ie-25.sh bash setup-id-25.sh
Step 7: Verify
Check All Containers Are Running
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"Expected output:
NAMES STATUS PORTS od-25 Up X mins 10.128.0.83:25-25/tcp dlp-out-25 Up X mins 10.128.0.84:25-25/tcp oe-25 Up X mins 10.128.0.85:25-25/tcp ie-25 Up X mins 10.128.0.86:25-25/tcp id-25 Up X mins 10.128.0.87:25-25/tcp
Test SMTP Connectivity Per Container
for ip in 10.128.0.83 10.128.0.84 10.128.0.85 10.128.0.86 10.128.0.87; do echo "Testing $ip:25..." nc -zv $ip 25 2&1 | grep -E "succeeded|refused|timeout" done
Troubleshooting
| Issue | Fix |
|---|---|
address already in use on port 25 |
Stop/remove existing container → docker stop <name> && docker rm <name>
|
| Alias IPs lost after reboot | Enable the alias-ips.service systemd unit from Step 3 |
| Container exits immediately | Check logs → docker logs <name> — usually a missing/misconfigured .env file |
IP not visible on ens4
|
GCP alias IP not saved — recheck GCP Console → Edit Instance → Alias IP Ranges |
| Can't bind to alias IP | Run ip a show ens4 — confirm IP was added with sudo ip addr add
|