Load Balancing VMware Horizon's UDP and TCP Traffic: A Guide with HAProxy

If you’ve worked with VMware Horizon (now Omnissa Horizon), you know it’s a common way for enterprise users to connect to remote desktops. But for IT engineers and DevOps teams? It’s a whole different story. Horizon’s custom protocols and complex connection requirements make load balancing a bit tricky. 

With its recent sale to Omnissa, the technology hasn’t changed—but neither has the headache of managing it effectively. Let’s break down the problem and explain why Horizon can be such a beast to work with… and how HAProxy can help.

What Is Omnissa Horizon?

Horizon is a remote desktop solution that provides users with secure access to their desktops and applications from virtually anywhere. It is known for its performance, flexibility, and enterprise-level capabilities. Here’s how a typical Horizon session works:

  1. Client Authentication: The client initiates a TCP connection to the server for authentication.

  2. Server Response: The server responds with details about which backend server the client should connect to.

  3. Session Establishment: The client establishes one TCP connection and two UDP connections to the designated backend server.

The problem? In order to maintain session integrity, all three connections must be routed to the same backend server. But Horizon’s protocol doesn’t make this easy. The custom protocol relies on a mix of TCP and UDP, which have fundamentally different characteristics, creating unique challenges for load balancing.

Why Load Balancing Omnissa Horizon Is So Difficult

The Multi-Connection Challenge

Since these connections belong to the same client session, they must route to the same backend server. A single misrouted connection can disrupt the entire session. For a load balancer, this is easier said than done.

The Problem with UDP

UDP is stateless, which means it doesn’t maintain any session information between the client and server. This is in stark contrast to TCP, which ensures state through its connection-oriented protocol. Horizon’s use of UDP complicates things further because:

  • There’s no built-in mechanism to track sessions.

  • Load balancers can’t use traditional stateful methods to ensure all connections from a client go to the same server.

  • Maintaining session stickiness for UDP typically requires workarounds that add complexity (like an external data source).

Traditional Load Balancing Falls Short

Most load balancers rely on session stickiness (or affinity) to route traffic consistently. In TCP, this is often achieved with in-memory client-server mappings, such as with HAProxy's stick tables feature. However, since UDP is stateless and doesn't track sessions like TCP does, stick tables do not support UDP. Keeping everything coordinated without explicit session tracking feels like solving a puzzle without all the pieces—and that’s where the frustration starts. 

This is why Omnissa (VMWare) suggests using their “Unified Access Gateway” (UAG) appliance to handle the connections. While this makes one problem easier, it adds another layer of cost and complexity to your network. While you may need the UAG for a more comprehensive solution for Omnissa products, it would be great if there was a simpler, cleaner, and more efficient solution.

This leaves engineers with a critical question: How do you achieve session stickiness for a stateless protocol? This is where HAProxy offers an elegant solution.

Enter HAProxy: A Stateless Approach to Stickiness

HAProxy’s balance-source algorithm is the key to solving the Horizon multi-protocol challenge. This approach uses consistent hashing to achieve session stickiness without relying on stateful mechanisms like stick tables. From the documentation:

“The source IP address is hashed and divided by the total weight of the running servers to designate which server will receive the request. This ensures that the same client IP address will always reach the same server as long as no server goes down or up.” 

Here’s how it works:

  1. Hashing Client IP: HAProxy computes a hash of the client’s source IP address.

  2. Mapping to Backend Servers: The hash is mapped to a specific backend server in the pool.

  3. Consistency Across Connections: The same client IP will always map to the same backend server.

This deterministic, stateless approach ensures that all connections from a client—whether TCP or UDP—are routed to the same server, preserving session integrity.

Why Stateless Stickiness Works

The beauty of HAProxy’s solution lies in its simplicity and efficiency—it has low overhead, works for both protocols and is tolerant to changes. Changes to the server pool may cause the connections to rebalance, but those clients will be redirected consistently as noted in the documentation:

“If the hash result changes due to the number of running servers changing, many clients will be directed to a different server.”

It is super efficient because there is no need for in-memory storage or synchronization between load balancers. The same algorithm works seamlessly for both TCP and UDP. 

This stateless method doesn’t just solve the problem; it does so elegantly, reducing complexity and improving reliability.

HAProxy UDP

Implementing HAProxy for Omnissa Horizon

While the configuration is relatively straightforward, we will need the HAProxy Enterprise UDP Module to provide UDP load balancing. This module is included in HAProxy Enterprise, which adds additional enterprise functionality and ultra-low-latency security layers on top of our open-source core.

New to HAProxy Enterprise?

HAProxy Enterprise provides high-performance load balancing for TCP, UDP, QUIC, and HTTP-based applications, high availability, an API/AI gateway, Kubernetes application routing, SSL processing, DDoS protection, bot management, global rate limiting, and a next-generation WAF.


HAProxy Enterprise combines the performance, reliability, and flexibility of our open-source core (HAProxy – the most widely used software load balancer) with ultra-low-latency security layers and world-class support.

Implementation Overview

So, how easy is it to implement? Just a few lines of configuration will get you what you need. You start by defining your frontend and backend, and then add the “magic”:

  1. Define Your Frontend and Backend: The frontend section handles incoming connections, while the backend defines how traffic is distributed to servers.

  2. Enable Balance Source: The balance source directive ensures that HAProxy computes a hash of the client’s IP and maps it to a backend server.

  3. Optimize Health Checks: Include the check keyword for backend servers to enable health checks. This ensures that only healthy servers receive traffic.

  4. UDP Load Balancing: The UDP module in the enterprise edition is necessary for UDP load balancing, and uses the udp-lb keyword. 

Here’s what a basic configuration might look like for the custom “Blast” protocol:

frontend ft_horizon_tcp_blast
  bind *:22443
  default_backend bk_horizon_tcp_blast
  backend bk_horizon_tcp_blast
  server srv1 192.168.1.101:22443 check
  server srv2 192.168.1.102:22443 check
  balance source

udp-lb horizon_udp_blast
  dgram-bind *:22443
  server srv1 192.168.1.101:22443 check
  server srv2 192.168.1.102:22443 check
  balance source

udp-lb horizon_udp_blast_pcoip
  dgram-bind *:4172
  server srv1 192.168.1.101:4172 check
  server srv2 192.168.1.102:4172 check
  balance source

This setup ensures that all incoming connections—whether TCP or UDP—are mapped to the same backend server based on the client’s IP address. The hash-type consistent option minimizes disruption during server pool changes.

This approach is elegant in its simplicity. We use minimal configuration, but we still get a solid approach to session stickiness. It is also incredibly performant, keeping memory usage and CPU demands low. Best of all, it is highly reliable, with consistent hashing ensuring stable session persistence, even when servers are added or removed.

Advanced Options in HAProxy 3.0+

HAProxy 3.0 introduced enhancements that make this approach even better. It offers more granular control over hashing, allowing you to specify the hash key (e.g., source IP or source+port). This is particularly useful for scenarios where IP addresses may overlap or when the list of servers is in a different order.

We can also include hash-balance-factor, which will help keep any individual server from being overloaded. From the documentation:

“Specifying a "hash-balance-factor" for a server with "hash-type consistent" enables an algorithm that prevents any one server from getting too many requests at once, even if some hash buckets receive many more requests than others. 

[...]

If the first-choice server is disqualified, the algorithm will choose another server based on the request hash, until a server with additional capacity is found.”

Finally, we can adjust the hash function to be used for the hash-type consistent option. This defaults to sdbm, but there are 4 functions and an optional none if you want to manually hash it yourself. See the documentation for details on these functions.

Sample configuration using advanced options:

backend bk_horizon_tcp_blast
  server srv1 192.168.1.101:22443 check
  server srv2 192.168.1.102:22443 check
  balance source
  hash-type consistent sdbm
  hash-key addr-port
  hash-balance-factor 150

These features improve flexibility and reduce the risk of uneven traffic distribution across backend servers.

Coordination Without Coordination

The genius of HAProxy’s solution lies in its stateless state. By relying on consistent algorithms, it achieves an elegant solution that many would assume requires complex session tracking or external databases. This approach is not only efficient but also scalable.

The result? A system that feels like it’s maintaining state without actually doing so. It’s like a magician revealing their trick—it’s simpler than it looks, but still impressive.

Understanding Omnissa Horizon’s challenges is half the battle. Implementing a solution can be surprisingly straightforward with HAProxy. You can ensure reliable load balancing for even the most complex protocols by leveraging stateless stickiness through consistent hashing.

This setup not only solves the Horizon problem but also demonstrates the power of HAProxy as a versatile tool for DevOps and IT engineers. Whether you’re managing legacy applications or cutting-edge deployments, HAProxy has the features to make your life easier.


FAQ

1. Why can’t I use stick tables for Horizon?
Stick tables work well for TCP but aren’t compatible with Horizon’s UDP requirements. Since UDP is stateless, stick tables can’t track sessions effectively across multiple protocols.

2. What happens if a server goes down?
With consistent hashing, only clients assigned to the failed server are redirected. Other clients remain unaffected, minimizing disruption.

3. Can I change server weights with this setup?
Yes, but consistent hashing may not perfectly distribute traffic by weight. If precise load balancing is critical, explore dynamic rebalancing options.

4. What’s the difference between balance source and other algorithms?
The balance source algorithm is deterministic and maps client IPs to backend servers using a hash function. Other algorithms, like round-robin, distribute traffic evenly but don’t guarantee session stickiness.

5. Can HAProxy handle changes in client IPs, such as those caused by NAT or VPNs?
While the balance source algorithm relies on the client’s IP, using hash-key options like addr-port can help mitigate issues caused by NAT or VPNs by factoring in the client’s port along with the IP address.

6. How does HAProxy compare to Omnissa’s Unified Access Gateway (UAG) for load-balancing Horizon?
Omnissa’s UAG offers a Horizon-specific solution with built-in features such as authentication and seamless integration with Horizon environments. It is designed for organizations that require an all-in-one solution with added security and user management capabilities. On the other hand, HAProxy provides a highly efficient, cost-effective load-balancing solution with robust support for SSL termination, advanced traffic management, and high availability. It is an ideal choice for environments that prioritize flexibility, performance, and customization without the additional overhead of UAG’s specialized features.

7. Is this solution future-proof?
Yes! HAProxy continues to evolve, and its consistent hashing features are robust enough to handle most Horizon deployments. Future enhancements may add even more flexibility for UDP handling.


Resources


Subscribe to our blog. Get the latest release updates, tutorials, and deep-dives from HAProxy experts.