DNS-name Targets
Use a hostname instead of an IP literal as the rule target. Cached, single-flighted, RFC-8767 stale-while-error.
Available since v0.3.0. The target host in any push-rule may be a DNS
name (e.g. api.example.com:443) instead of an IP literal.
How it works
- Resolution happens lazily on first connect through
hickory-resolverreading/etc/resolv.conf. - Results cache per the resolver-reported TTL clamped to
[5 s, 5 min]. The cache is bounded (default 8192 entries) so high name-cardinality workloads can't grow it without limit; expired entries are evicted first, then the live entries closest to expiry if still over the cap. - On refresh failure the rule stays
Activeand the last-known answer continues serving for 30 s of grace (RFC 8767 stale-while-error). After grace expiry, a fresh attempt is allowed every 3 s. - Per-rule single-flight collapses concurrent first-connects to ONE upstream resolver call.
- Multi-A/AAAA fallback tries each returned address in family-preference order — a single dead IP doesn't fail the connection.
- IPv4-first by default; pass
--prefer-ipv6to flip per rule.
Push a DNS-target rule
# IPv4-first (default)
portunus-server push-rule edge-01 8443 api.example.com:443
# AAAA-first; falls back to A if no AAAA
portunus-server push-rule edge-01 8444 api.example.com:443 --prefer-ipv6Hot path
IP-literal targets skip the resolver entirely: IpAddr::from_str
short-circuits straight to the dial. The dial itself is time-boxed
by the per-attempt timeout (3 s), so a SYN-blackhole target (a firewall
that drops rather than refuses) fails fast instead of wedging a task in
kernel SYN-retransmit for ~75–127 s.
DNS-target hot path performance:
- Cache-hit lookup: ~75 ns median (one async-mutex acquire + HashMap get + Vec clone).
- 100 concurrent first-connects to the same hostname: resolver invoked exactly once.
Failure visibility
DNS failures are exposed two ways:
# Per-rule field
portunus-server rule-stats <id>
# rule_id=… dns_failures=3 …
# Prometheus
curl -s 127.0.0.1:7081/metrics | grep dns_failures
# portunus_rule_dns_failures_total{client="edge-01",owner="alice",rule="2"} 3The collector is one row per rule (not one per failure), preserving
the cardinality budget. The row is removed alongside
rule_active_connections on remove-rule.
Resolver tunables
Currently spec-fixed (no operator overrides yet). Listed here for
visibility; a future release may surface them under [resolver] in
server.toml:
| Knob | Value |
|---|---|
cache_floor | 5 s |
cache_ceiling | 5 min |
stale_while_error_grace | 30 s |
attempt_timeout | 3 s |
negative_cache_retry | 3 s |
max_concurrent_resolves | 64 |
max_cache_entries | 8192 |