CoreDNS-GSLB: Health Checks
The GSLB plugin supports several types of health checks for backends. Each type can be configured per backend in the YAML configuration file.
Additionally, the GSLB plugin automatically adapts the healthcheck interval for each DNS record based on recent resolution activity.
- If a record is not resolved (queried) for a duration longer than
resolution_idle_timeout, the healthcheck interval for its backends is multiplied byhealthcheck_idle_multiplier(default: 10, configurable in the Corefile). - As soon as a DNS query is received for the record, the interval returns to its normal value (
scrape_interval). - This mechanism reduces unnecessary healthcheck traffic for rarely used records, while keeping healthchecks frequent for active records.
Example:
scrape_interval: 10s,resolution_idle_timeout: 3600s,healthcheck_idle_multiplier: 10- If no DNS query is received for 1 hour, healthchecks run every 100s instead of every 10s.
- When a query is received, healthchecks resume every 10s.
This feature helps optimize resource usage and backend load in large or dynamic environments.
Healthcheck Profiles
You can define reusable health check profiles at the top level of your YAML configuration using the healthcheck_profiles key. Each profile defines a health check type and its parameters. Backends can then reference these profiles by name in their healthchecks list, instead of repeating the same configuration.
Example:
healthcheck_profiles:
https_default:
type: http
params:
enable_tls: true
port: 443
uri: /
expected_code: 200
timeout: 5s
records:
webapp.example.com.:
backends:
- address: 10.0.0.1
healthchecks: [ https_default ] # Reference the profile by name
priority: 1
You can still use inline healthcheck definitions as before, or mix both approaches. If a backend's healthchecks list contains a string, it is interpreted as a profile name.
Global Healthcheck Profiles
You can define healthcheck profiles globally for all zones using the Corefile directive:
The file healthcheck_profiles.yml should contain:
healthcheck_profiles:
https_default:
type: http
params:
enable_tls: true
port: 443
uri: /
expected_code: 200
timeout: 5s
- Global profiles are available to all YAML zone files.
- If a profile with the same name exists locally in a zone YAML, the local one takes precedence.
- You can reference a profile by name in any backend's
healthcheckslist.
Healthcheck Types
HTTP(S)
Checks the health of an HTTP or HTTPS endpoint by making a request and validating the response code and/or body.
The HTTP health check connects to backend.address on given params.port. The Host header is set based on params.host, which does not overwrite the target address. HTTP forwards are not followed.
mTLS is enabled by setting params.enable_tls to true and then supplying both of params.cert_path and params.key_path there is the optional params.ca_path as well for using private PKI, this can be the Root CA or the chain depending on your server implementation of certificate checking strictness. If the CA path is omitted, the trusted root store is used.
If params.enable_tls is not enabled, mTLS will not be attempted, be explicit in your configuration intention!
Also, if params.ca_path is set but params.skip_tls_verify is set to true, it will have a net-zero effect.
Additionally, the params.host is used as the SNI parameter in the Client Hello for multiplexed server targets.
Debugging HTTP health checks is easier when the user agent is clearly visible in the server logs, so a predefined User-Agent value is set to CoreDNS/GSLB/....
healthchecks:
- type: http
params:
port: 443 # Port to connect (443 for HTTPS, 80 for HTTP)
uri: "/" # URI to request
method: "GET" # HTTP method
host: "localhost" # Host header for the request
headers: # Additional HTTP headers (key-value pairs)
timeout: 5s # Timeout for the HTTP request
expected_code: 200 # Expected HTTP status code
expected_body: "" # Expected response body (empty means no body validation)
enable_tls: true # Use TLS for the health check (HTTPS)
cert_path: "cert.pem" # Define path to client certificate for mTLS
key_path: "key.pem" # Define path for client private key for mTLS
ca_path: "cacert.pem" # Define optional path to trusted Private RootCA File or Chain
skip_tls_verify: true # Skip TLS certificate validation
TCP
Checks if a TCP connection can be established to the backend on a given port.
healthchecks:
- type: tcp
params:
port: 80 # TCP port to connect to
timeout: "3s" # Connection timeout
ICMP (Ping)
Checks if the backend responds to ICMP echo requests (ping).
healthchecks:
- type: icmp
params:
timeout: 2s # Timeout for the ICMP request
count: 3 # Number of ICMP requests to send
MySQL
Checks MySQL server health by connecting and executing a query.
healthchecks:
- type: mysql
params:
host: "10.0.0.5" # MySQL server address
port: 3306 # MySQL port
user: "gslbcheck" # Username
password: "secret" # Password
database: "test" # Database to connect
timeout: "3s" # Connection/query timeout
query: "SELECT 1" # Query to execute (optional, default: SELECT 1)
gRPC
Checks the health of a gRPC service using the standard gRPC health checking protocol (grpc.health.v1.Health/Check). It supports secure connections via TLS and mutual TLS (mTLS) client verification.
healthchecks:
- type: grpc
params:
port: 9090 # gRPC port to connect to
service: "grpc.health.v1.Health" # Service name (default: "")
timeout: 5s # Timeout for the gRPC request
enable_tls: true # Enable TLS/mTLS (default: false)
cert_path: "cert.pem" # Path to client certificate for mTLS (optional)
key_path: "key.pem" # Path to client private key for mTLS (optional)
ca_path: "cacert.pem" # Path to trusted CA Root certificate (optional)
skip_tls_verify: false # Skip TLS certificate validation (default: false)
servicecan be left empty to check the overall server health, or set to a specific service name.enable_tlsmust be set totrueto enable encrypted TLS transport.cert_pathandkey_pathare required when client certificate verification (mTLS) is enforced by the gRPC server.ca_pathallows specifying a private or custom CA root chain rather than the system's default trust store.
Lua Scripting
Executes an embedded Lua script to determine the backend health. The script can use the helper functions http_get(url) and json_decode(str) to perform HTTP requests and parse JSON. The global variable 'backend' provides the backend's address and priority.
Available helpers:
http_get(url, [timeout_sec], [user], [password], [tls_verify]): Performs an HTTP(S) GET request. Optional timeout (seconds), HTTP Basic auth (user, password), and TLS verification (default true).json_decode(str): Parses a JSON string and returns a Lua table (or nil on error).metric_get(url, metric_name, [timeout_sec], [tls_verify], [user], [password]): Fetches the value of a Prometheus metric from a /metrics endpoint (returns the first value found as a number or string, or nil if not found). Optional timeout (seconds), TLS verification (default true), and HTTP Basic auth (user, password).ssh_exec(host, user, password_or_key_path, command, [timeout_sec], [key_passphrase]): Executes a command via SSH and returns the output as a string. If the third parameter points to a valid file path, it is treated as the path to an SSH private key file. Optional timeout (seconds) and key passphrase.backend: A Lua table with fields:address: the backend's address (string)priority: the backend's priority (number)
Example: Use http_get and json_decode
healthchecks:
- type: lua
params:
timeout: 5s
script: |
local health = json_decode(http_get("http://" .. backend.address .. ":9200/_cluster/health"))
if health and health.status == "green" and health.number_of_nodes >= 3 then
return true
else
return false
end
Example: Get a Prometheus metric value
healthchecks:
- type: lua
params:
timeout: 5s
script: |
local value = metric_get("http://myapp:9100/metrics", "nginx_connections_active")
if value and value < 100 then
return true
end
return false
Example: Check a process via SSH
healthchecks:
- type: lua
params:
timeout: 5s
script: |
local output = ssh_exec("10.0.0.5", "monitor", "secret", "pgrep nginx")
if output and output ~= "" then
return true
else
return false
end
Example: metric_get with timeout and skip TLS verification
healthchecks:
- type: lua
params:
timeout: 5s
script: |
local value = metric_get("https://myapp:9100/metrics", "nginx_connections_active", 2, false)
if value and value < 100 then
return true
end
return false
Example: ssh_exec with timeout
healthchecks:
- type: lua
params:
timeout: 5s
script: |
local out = ssh_exec("10.0.0.5", "user", "pass", "pgrep nginx", 3)
if out ~= "" then
return true
end
return false
Example: ssh_exec with SSH private key authentication
healthchecks:
- type: lua
params:
timeout: 5s
script: |
-- Pass the path to the private key file instead of a password.
-- Optional: pass the key's passphrase as the 6th argument.
local out = ssh_exec("10.0.0.5", "monitor", "/home/coredns/.ssh/id_rsa", "pgrep nginx", 3, "key-passphrase-if-any")
if out ~= "" then
return true
end
return false
Security Warning
Storing plaintext passwords directly in the YAML configuration or passing them via the REST API represents a major credential exposure and remote code execution (RCE) risk.
- Avoid hardcoding plaintext passwords inside
ssh_exec. - Prefer using SSH private key files and restrict the SSH user permissions on the target server (e.g., using a restricted shell or setting a
command="..."prefix in the destination'sauthorized_keysfile). - Secure the configuration files and the API endpoints with strict file permissions and strong basic authentication.
Example: metric_get with HTTP Basic authentication
healthchecks:
- type: lua
params:
timeout: 5s
script: |
local value = metric_get("https://myapp:9100/metrics", "nginx_connections_active", 2, true, "user", "pass")
if value and value < 100 then
return true
end
return false
Rise and Fall Thresholds
To prevent healthcheck flapping due to transient network issues, you can configure consecutive success/failure thresholds to transition a backend's status.
fall(default:3): The number of consecutive failed checks required to mark an active backend as offline (DOWN).rise(default:2): The number of consecutive successful checks required to mark an offline backend as online (UP).
These thresholds can be defined globally in a healthcheck profile, or overridden on a per-backend basis.
graph LR
subgraph UP_State ["State: UP (Online)"]
A[Healthy] -->|"Failed check"| B(Increment failures)
B -->|"Failures < fall"| A
A -->|"Successful check"| A_Reset(Reset failure counter)
end
subgraph DOWN_State ["State: DOWN (Offline)"]
C[Unhealthy] -->|"Successful check"| D(Increment successes)
D -->|"Successes < rise"| C
C -->|"Failed check"| C_Reset(Reset success counter)
end
B -->|"Failures == fall (default: 3)"| C
D -->|"Successes == rise (default: 2)"| A
style A fill:#dcfce7,stroke:#16a34a,stroke-width:1px,color:#14532d
style C fill:#fee2e2,stroke:#dc2626,stroke-width:1px,color:#7f1d1d
Thresholds Configured via Profiles example:
healthcheck_profiles:
http_flapping_prevented:
type: http
rise: 4
fall: 5
params:
port: 8080
uri: /health
Thresholds Overridden Per-Backend example:
records:
webapp.example.com.:
backends:
- address: "10.0.0.5"
healthchecks: [ http_flapping_prevented ]
rise: 2 # overrides the profile's rise value
fall: 3 # overrides the profile's fall value
Healthcheck Bypass
In multi-region setups, you might want to bypass health checking for remote backends (e.g. to prevent WAN/cross-region network blips from marking them as down in a local resolver). You can specify assume_healthy: true on any backend:
backends:
- address: "172.16.0.10"
healthchecks: [ https_default ] # Checked normally
- address: "172.16.0.20"
assume_healthy: true # No health probes executed, always assumed healthy
- If
assume_healthy: trueis set, no health checks are performed for this backend, andIsHealthy()always returnstrue(as long asenableis alsotrue). - It defaults to
false.