← Back

Lettuce DNS Resolution

JVM vs Netty resolver · EKS CoreDNS flow · FQDN vs non-FQDN — all interactive

1. Why switch from JVM to Netty DNS resolver? Lettuce 6.x
The JVM DNS resolver is synchronous and blocking — it blocks the Netty event loop thread during the DNS lookup. The Netty resolver is async and non-blocking — and it actually respects the TTL in DNS responses. Both problems compound each other in a high-throughput Redis cluster.
JVM Resolver (pre Lettuce 6.x default)
Uses InetAddress.getAllByName() · Click to see call stack
ThreadingBlocking — stalls event loop
DNS TTLIgnored — uses JVM cache TTL
JVM cache TTL default30s (or ∞ with SecurityManager)
Failover response timeUp to JVM TTL seconds
Event loop impactThread blocked during lookup
Call chain inside Lettuce event loop
RedisClusterClient.connect()
DefaultEndpoint.connect()
BLOCKSInetAddress.getAllByName("my-redis.cache.amazonaws.com")
BLOCKSOS DNS syscall — waits for network response
returns InetAddress[] — event loop resumes
Event loop during DNS lookup
EL-0
EL-1
EL-2
EL-3
Netty DNS Resolver (Lettuce 6.x recommended)
Uses Netty's DnsAddressResolverGroup · Click to see call chain
ThreadingNon-blocking — event loop stays free
DNS TTLRespected — re-resolves when TTL expires
Cache TTL sourceFrom DNS response (e.g. 5s for ElastiCache)
Failover response timeWithin DNS record TTL seconds
Event loop impactNone — async Future callback
Call chain inside Lettuce event loop
RedisClusterClient.connect()
DefaultEndpoint.connect()
ASYNCDnsAddressResolverGroup.resolve("my-redis.cache.amazonaws.com")
ASYNCSends DNS UDP packet → event loop thread freed immediately
…handles other Redis I/O…
ASYNCDNS response arrives → Future callback → connect proceeds
Event loop during DNS lookup
EL-0
EL-1
EL-2
EL-3
2. Failover simulator — watch JVM hold stale IP while Netty re-resolves interactive
AWS ElastiCache sets a short DNS TTL (1–5s) so clients re-resolve quickly after a failover. The JVM ignores this and holds the old IP for its own configured TTL. Trigger a failover and watch the difference.
DNS Record (what the authoritative DNS server has)
my-redis.abc123.ng.0001.euw1.cache.amazonaws.com
10.0.1.5 Type A, TTL=5s
TTL countdown: 5.0s
active
JVM Client (InetAddress cache)
Cached IP:10.0.1.5
Cache age:0.0s
Cache TTL:30s
Time until re-resolve
✓ Connected to Redis
Netty DNS Client (respects record TTL)
Cached IP:10.0.1.5
TTL remaining:5.0s
Source:DNS response
Time until re-resolve
✓ Connected to Redis
3. EKS DNS resolution flow step-by-step
Pick a hostname and watch the query travel through each layer. Toggle between an external hostname (ElastiCache) and an in-cluster Redis service.
Your App Pod (Lettuce client)
resolving hostname via /etc/resolv.conf → nameserver 10.96.0.10
/etc/hosts
checked first — loopback, node hostname overrides
CoreDNS (kube-dns) — 10.96.0.10
knows all cluster services · caches external lookups · forwards unknown to VPC DNS
AWS VPC DNS — 169.254.169.253
resolves private hosted zones (Route 53 private) + forwards public
Route 53 Public DNS
authoritative for *.cache.amazonaws.com and other AWS domains
What happened
Press ▶ Resolve to trace the query.
4. FQDN vs non-FQDN — the ndots:5 query explosion interactive
Kubernetes sets ndots:5 by default. If your hostname has fewer than 5 dots, CoreDNS tries every search domain before treating it as absolute. Type a name and press Resolve to see both paths.
non-FQDN (no trailing dot)
# ready — press ▶ Resolve both
FQDN (with trailing dot)
# ready — press ▶ Resolve both
5. EKS /etc/resolv.conf — live preview
Every EKS pod gets this injected automatically. The ndots value drives the FQDN expansion above. Adjust the controls to see how it changes.
The search list is tried in order when a name has fewer than 5 dots. With redis-master (0 dots) all 4 domains get tried before the bare name — that's 5 DNS lookups.

Reference

Configure Lettuce to use the Netty DNS resolver
ClientResources resources = DefaultClientResources.builder()
    // Use Netty's async DNS resolver instead of InetAddress
    .dnsResolver(new DnsResolvers.NettyDnsResolver())
    // Refresh cluster topology when DNS changes
    .build();

RedisClusterClient client = RedisClusterClient.create(
    resources, "redis://my-redis.cache.amazonaws.com");

Or with Spring Data Redis, configure the LettuceClientConfigurationBuilderCustomizer bean.

Override JVM DNS TTL (stopgap if you can't upgrade Lettuce)
# In $JAVA_HOME/conf/security/java.security
networkaddress.cache.ttl=5
networkaddress.cache.negative.ttl=1

# Or as JVM argument at startup
-Dsun.net.inetaddr.ttl=5

This affects the entire JVM — all DNS lookups, not just Lettuce. The Netty resolver approach is scoped to Lettuce only and is generally preferred.

Fix the FQDN / ndots problem in EKS
# Option 1: always use FQDN with trailing dot in your connection string
redis-master.redis.svc.cluster.local.

# Option 2: set ndots:1 in your pod spec (most effective)
spec:
  dnsConfig:
    options:
      - name: ndots
        value: "1"

# Option 3: set ndots:2 — still avoids most explosion but keeps
# short cluster-internal names working

Setting ndots:1 means any name with 1 or more dots is treated as absolute first — practically eliminates spurious queries for ElastiCache endpoints which already have many dots.

AWS ElastiCache DNS specifics
CoreDNS caching in EKS