Skip to content

DNS active health checks for NGINX Plus #442

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 21 additions & 19 deletions content/nginx/admin-guide/load-balancer/tcp-udp-load-balancer.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,23 @@ type:
- how-to
---

<span id="intro"></span>
## Introduction
## Introduction {#intro}

[Load balancing](https://www.nginx.com/solutions/load-balancing/) refers to efficiently distributing network traffic across multiple backend servers.
[Load balancing](https://www.f5.com/glossary/load-balancer) refers to efficiently distributing network traffic across multiple backend servers.

In F5 NGINX Plus [R5]({{< ref "nginx/releases.md#r5" >}}) and later, NGINX Plus can proxy and load balance Transmission Control Protocol) (TCP) traffic. TCP is the protocol for many popular applications and services, such as LDAP, MySQL, and RTMP.

In NGINX Plus [R9]({{< ref "nginx/releases.md#r9" >}}) and later, NGINX Plus can proxy and load balance UDP traffic. UDP (User Datagram Protocol) is the protocol for many popular non-transactional applications, such as DNS, syslog, and RADIUS.

To load balance HTTP traffic, refer to the [HTTP Load Balancing]({{< ref "http-load-balancer.md" >}}) article.

<span id="prerequisites"></span>
## Prerequisites

- Latest NGINX Plus (no extra build steps required) or latest [NGINX Open Source](https://nginx.org/en/download.html) built with the `--with-stream` configuration flag
- An application, database, or service that communicates over TCP or UDP
- Upstream servers, each running the same instance of the application, database, or service

<span id="proxy_pass"></span>
## Configuring Reverse Proxy
## Configuring reverse proxy {#proxy_pass}

First, you will need to configure _reverse proxy_ so that NGINX Plus or NGINX Open Source can forward TCP connections or UDP datagrams from clients to an upstream group or a proxied server.

Expand Down Expand Up @@ -118,8 +115,7 @@ Open the NGINX configuration file and perform the following steps:
}
```

<span id="upstream"></span>
## Configuring TCP or UDP Load Balancing
## Configuring TCP or UDP load balancing {#upstream}

To configure load balancing:

Expand Down Expand Up @@ -250,17 +246,15 @@ stream {
}
```

<span id="health"></span>
## Configuring Health Checks
## Configuring health checks {#health}

NGINX can continually test your TCP or UDP upstream servers, avoid the servers that have failed, and gracefully add the recovered servers into the load‑balanced group.

See [TCP Health Checks]({{< ref "nginx/admin-guide/load-balancer/tcp-health-check.md" >}}) for instructions how to configure health checks for TCP.

See [UDP Health Checks]({{< ref "nginx/admin-guide/load-balancer/udp-health-check.md" >}}) for instructions how to configure health checks for UDP.

<span id="on-the-fly-configuration"></span>
## On-the-Fly Configuration
## On-the-fly configuration

Upstream server groups can be easily reconfigured on-the-fly using NGINX Plus REST API. Using this interface, you can view all servers in an upstream group or a particular server, modify server parameters, and add or remove upstream servers.

Expand Down Expand Up @@ -355,8 +349,7 @@ To enable on-the-fly configuration:
}
```

<span id="on-the-fly-configuration-example"></span>
### On-the-Fly Configuration Example
### On-the-fly configuration example

```nginx
stream {
Expand Down Expand Up @@ -403,23 +396,22 @@ For example, to add a new server to the server group, send a `POST` request:
curl -X POST -d '{ \
"server": "appserv3.example.com:12345", \
"weight": 4 \
}' -s 'http://127.0.0.1/api/6/stream/upstreams/appservers/servers'
}' -s 'http://127.0.0.1/api/9/stream/upstreams/appservers/servers'
```

To remove a server from the server group, send a `DELETE` request:

```shell
curl -X DELETE -s 'http://127.0.0.1/api/6/stream/upstreams/appservers/servers/0'
curl -X DELETE -s 'http://127.0.0.1/api/9/stream/upstreams/appservers/servers/0'
```

To modify a parameter for a specific server, send a `PATCH` request:

```shell
curl -X PATCH -d '{ "down": true }' -s 'http://127.0.0.1/api/6/http/upstreams/appservers/servers/0'
curl -X PATCH -d '{ "down": true }' -s 'http://127.0.0.1/api/9/http/upstreams/appservers/servers/0'
```

<span id="example"></span>
## Example of TCP and UDP Load-Balancing Configuration
## Example of TCP and UDP load-balancing configuration {#example}

This is a configuration example of TCP and UDP load balancing with NGINX:

Expand Down Expand Up @@ -471,3 +463,13 @@ The three [`server`](https://nginx.org/en/docs/stream/ngx_stream_upstream_module
- The second server listens on port 53 and proxies all UDP datagrams (the `udp` parameter to the `listen` directive) to an upstream group called **dns_servers**. If the `udp` parameter is not specified, the socket listens for TCP connections.

- The third virtual server listens on port 12346 and proxies TCP connections to **backend4.example.com**, which can resolve to several IP addresses that are load balanced with the Round Robin method.

## See also

- [TCP Health Checks]({{< relref "tcp-health-check.md" >}})

- [UDP Health Checks]({{< relref "udp-health-check.md" >}})

- [Load Balancing DNS Traffic with NGINX and NGINX Plus](https://www.f5.com/company/blog/nginx/load-balancing-dns-traffic-nginx-plus)

- [TCP/UDP Load Balancing with NGINX: Overview, Tips, and Tricks](https://blog.nginx.org/blog/tcp-load-balancing-udp-load-balancing-nginx-tips-tricks)
160 changes: 141 additions & 19 deletions content/nginx/admin-guide/load-balancer/udp-health-check.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ type:
- how-to
---

<span id="prereq"></span>
## Prerequisites
NGINX Plus can continually test your upstream servers that handle UDP network traffic (DNS, RADIUS, syslog), avoid the servers that have failed, and gracefully add the recovered servers into the load‑balanced group.

- You have configured an upstream group of servers that handles UDP network traffic (DNS, RADIUS, syslog) in the [`stream {}`](https://nginx.org/en/docs/stream/ngx_stream_core_module.html#stream) context, for example:
## Prerequisites {#prereq}

- You have [configured an upstream group of servers]({{< ref "nginx/admin-guide/load-balancer/tcp-udp-load-balancer.md" >}}) that handles UDP network traffic in the [`stream {}`](https://nginx.org/en/docs/stream/ngx_stream_core_module.html#stream) context, for example:

```nginx
stream {
Expand Down Expand Up @@ -44,8 +45,7 @@ type:

See [TCP and UDP Load Balancing]({{< ref "nginx/admin-guide/load-balancer/tcp-udp-load-balancer.md" >}}) for details.

<span id="hc_passive"></span>
## Passive UDP Health Checks
## Passive UDP health checks {#hc_passive}

NGINX Open Source or F5 NGINX Plus can mark the server as unavailable and stop sending UDP datagrams to it for some time if the server replies with an error or times out.

Expand All @@ -62,8 +62,7 @@ upstream dns_upstream {
}
```

<span id="hc_active"></span>
## Active UDP Health Checks
## Active UDP health checks {#hc_active}

Active Health Checks allow testing a wider range of failure types and are available only for NGINX Plus. For example, instead of waiting for an actual TCP request from a DNS client to fail before marking the DNS server as down (as in passive health checks), NGINX Plus will send special health check requests to each upstream server and check for a response that satisfies certain conditions. If a connection to the server cannot be established, the health check fails, and the server is considered unhealthy. NGINX Plus does not proxy client connections to unhealthy servers. If more than one health check is defined, the failure of any check is enough to consider the corresponding upstream server unhealthy.

Expand Down Expand Up @@ -100,8 +99,7 @@ To enable active health checks:

A basic UDP health check assumes that NGINX Plus sends the “nginx health check” string to an upstream server and expects the absence of ICMP “Destination Unreachable” message in response. You can configure your own health check tests in the `match {}` block. See [The “match {}” Configuration Block](#hc_active_match) for details.

<span id="hc_active_finetune"></span>
### Fine-Tuning UDP Health Checks
### Fine-Tuning UDP Health Checks {#hc_active_finetune}

You can fine‑tune the health check by specifying the following parameters to the [`health_check`](https://nginx.org/en/docs/stream/ngx_stream_upstream_hc_module.html#health_check) directive:

Expand All @@ -119,10 +117,9 @@ server {

In the example, the time between UDP health checks is increased to 20 seconds, the server is considered unhealthy after 2 consecutive failed health checks, and the server needs to pass 2 consecutive checks to be considered healthy again.

<span id="hc_active_match"></span>
### The “match {}” Configuration Block
### The “match {}” configuration block {#hc_active_match}

You can verify server responses to health checks by configuring a number of tests. These tests are defined within the [`match {}`](https://nginx.org/en/docs/stream/ngx_stream_upstream_hc_module.html#match) configuration block.
A basic UDP health check assumes that NGINX Plus sends the “nginx health check” string to an upstream server and expects the absence of ICMP “Destination Unreachable” message in response. You can configure your own health check tests that will verify server responses. These tests are defined within the [`match {}`](https://nginx.org/en/docs/stream/ngx_stream_upstream_hc_module.html#match) configuration block.

1. In the top‑level `stream {}` context, specify the [`match {}`](https://nginx.org/en/docs/stream/ngx_stream_upstream_hc_module.html#match) block and set its name, for example, `udp_test`:

Expand Down Expand Up @@ -155,8 +152,9 @@ You can verify server responses to health checks by configuring a number of test

These parameters can be used in different combinations, but no more than one `send` and one `expect` parameter can be specified at a time.

<span id="example_ntp"></span>
#### Example Test for NTP
## Usage scenarios

### NTP health checks {#example_ntp}

To fine‑tune health checks for NTP, you should specify both `send` and `expect` parameters with the following text strings:

Expand All @@ -167,14 +165,138 @@ match ntp {
}
```

<span id="example_dns"></span>
#### Example Test for DNS
#### Complete NTP health check configuration example

```nginx

stream {
upstream ntp_upstream {
zone ntp_zone 64k;
server 192.168.136.130:53;
server 192.168.136.131:53;
server 192.168.136.132:53;
}
server {
listen 53 udp;
proxy_pass ntp_upstream;
health_check match=ntp udp;
proxy_timeout 1s;
proxy_responses 1;
error_log logs/ntp.log;
}

match ntp {
send \xe3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00;
expect ~* \x24;
}
}
```

### DNS health checks {#example_dns}

[DNS health checks](#hc_active) can be enhanced to perform real DNS lookup queries. You can craft a valid DNS query packet, send it to the upstream server, and inspect the response to determine health.

The process includes three steps:
- [Creating a CNAME test record](#create-a-cname-record) on your DNS server.
- [Crafting a raw DNS query packet](#construct-a-raw-dns-query-packet) to be sent by NGINX Plus.
- [Validating the expected response](#configure-the-match-block-for-dns-lookup) using the `match` block, where the `send` parameter represents a raw DNS query packet, and `expect` represents the value of the CNAME record.

#### Create a CNAME record

First, create a CNAME record on your DNS server for a health check that points to the target website.

For example, if you are using BIND self-hosted DNS solution on a Linux server:

- Open the zone file in a text editor:

```shell
sudo nano /etc/bind/zones/db.example.com
```
- Add a CNAME record, making `healthcheck.example.com` resolve to `healthy.svcs.example.com`:

```none
healthcheck IN CNAME healthy.svcs.example.com.
```

- Save the file and reload the DNS service:

```shell
sudo systemctl reload bind9
```

Once the CNAME record is live and resolvable, you can craft a DNS query packet that represents a DNS lookup and can be used in the `send` directive.

#### Construct a raw DNS query packet

The `send` parameter of the `match` block allows you to send raw UDP packets for health checks. To query your CNAME record, you need to construct a valid DNS query packet according to the [DNS protocol message format](https://datatracker.ietf.org/doc/html/rfc1035#section-4.1), including a header and question section.

To fine‑tune health checks for DNS, you should also specify both `send` and `expect` parameters with the following text strings:
The DNS Query packet can be created using DNS packet builders, such as Python Scapy or dnslib, or packet analyzers, such as tcpdump or Wireshark. If using a packet analyzer, extract only the DNS layer, removing Ethernet, IP, and UDP-related headers.

This is the raw DNS query of `healthcheck.example.com`, represented as one line in Hex with `\x` prefixes:

```none
\x00\x01\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x0b\x68\x65\x61\x6c\x74\x68\x63\x68\x65\x63\x6b\x07\x65\x78\x61\x6d\x70\x6c\x65\x03\x63\x6f\x6d\x00\x00\x01\x00\x01
```
where:

{{<bootstrap-table "table table-striped table-bordered">}}
| HEX | Description |
|------------------|------------------------|
| \x00\x01 | Transaction ID: 0x0001 |
| \x01\x00 | Flags: Standard query, recursion desired |
| \x00\x01 | Questions: 1 |
| \x00\x00 | Answer RRs: 0 |
| \x00\x00 | Authority RRs: 0 |
| \x00\x00 | Additional RRs: 0 |
| \x0b\x68\x65\x61\x6c\x74\x68\x63\x68\x65\x63\x6b | "healthcheck" |
| \x07\x65\x78\x61\x6d\x70\x6c\x65 | "example" |
| \x03\x63\x6f\x6d | "com" |
| \x00 | end of name |
| \x00\x01 | Type: A |
| \x00\x01 | Class: IN |
{{</bootstrap-table>}}

#### Configure the match block for DNS lookup

Finally, specify the `match` block in the NGINX configuration file to pair the raw query with an expected response. The `send` directive should contain the DNS query packet, while `expect` directive should contain a matching DNS record in the DNS server's response:

```nginx
match dns {
send \x00\x2a\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\x73\x74\x6c\x04\x75\x6d\x73\x6c\x03\x65\x64\x75\x00\x00\x01\x00\x01;
expect ~* "health.is.good";
send \x00\x01\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x0b\x68\x65\x61\x6c\x74\x68\x63\x68\x65\x63\x6b\x07\x65\x78\x61\x6d\x70\x6c\x65\x03\x63\x6f\x6d\x00\x00\x01\x00\x01;
expect ~* "healthy.svcs.example.com";
}
```

#### Complete DNS health check configuration example

```nginx

stream {
upstream dns_upstream {
zone dns_zone 64k;
server 192.168.136.130:53;
server 192.168.136.131:53;
server 192.168.136.132:53;
}
server {
listen 53 udp;
proxy_pass dns_upstream;
health_check match=dns udp;
proxy_timeout 1s;
proxy_responses 1;
error_log logs/dns.log;
}

match dns {
# make sure appropriate CNAME record exists
send \x00\x01\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x0b\x68\x65\x61\x6c\x74\x68\x63\x68\x65\x63\x6b\x07\x65\x78\x61\x6d\x70\x6c\x65\x03\x63\x6f\x6d\x00\x00\x01\x00\x01;
expect ~* "healthy.svcs.example.com";
}
}
```

## See also

- [Load Balancing DNS Traffic with NGINX and NGINX Plus](https://www.f5.com/company/blog/nginx/load-balancing-dns-traffic-nginx-plus)

- [TCP/UDP Load Balancing with NGINX: Overview, Tips, and Tricks](https://blog.nginx.org/blog/tcp-load-balancing-udp-load-balancing-nginx-tips-tricks#activeHealthCheck)
Loading