dnsperf(1)
β¨ dnsperf β the manual, but make it cute β¨
okay bestie, so you found the dnsperf(1) page in the General Commands Manual and you're like "what is this". don't worry, i got you. this is part 1, so buckle up π
πΈ NAME πΈ
dnsperf β test the performance of a DNS server. that's it. that's the whole vibe. she's here to make your DNS server sweat π₯
π SYNOPSIS π
dnsperf [-a local_addr] [-b bufsize] [-B] [-c clients] [-d datafile]
[-D] [-e] [-E code:secret] [-f family] [-h] [-j] [-l limit] [-m mode]
[-n runs_through_file] [-p port] [-q num_queries] [-Q max_qps]
[-s server_addr] [-S stats_interval] [-t timeout] [-T threads] [-u] [-v]
[-W] [-x local_port] [-y [alg:]name:secret] [-O option=value]
yes that's a lot of flags. we love a girl with options π
π¦ DESCRIPTION π¦
so dnsperf is a DNS server performance testing tool, periodt. her main character energy is aimed at measuring how fast authoritative DNS servers go, but she can also measure caching server performance β only if you're doing it in a closed laboratory environment tho π. if you wanna test caching servers that resolve against the live Internet, then resperf is the program you actually want. different girl, different job.
now listen, big brain moment: you really should run dnsperf and the name server you're testing on separate machines. why? because if dnsperf itself is hogging the CPU on the same box, she's lowkey slowing down the name server and that ruins the whole point π. and those two machines should be connected with a fast network β preferably a dedicated Gigabit Ethernet segment (iconic, slay). testing through a router or firewall? don't. just don't. not advisable, bestie.
π§ Configuring the name server π§
if you're using dnsperf to test an authoritative server, set that name server up so it serves one or more zones that are similar in size and number to what the server is expected to serve in production. realistic vibes only β¨
also!! be sure to turn off recursion in the server's configuration. in BIND 8/9, that means specifying recursion no; in the options block. and in BIND 8 you should also specify fetch-glue no; β otherwise the server may try to go fetch glue information from the Internet during your test, which slows it down by an unpredictable factor. unpredictable?? we don't know her. no thank you π
π Constructing a query input file π
a dnsperf input file should contain a large and realistic set of queries β like, on the order of ten thousand to a million. big file, big dreams. the input file has one line per query, and each line is a domain name and an RR type name, separated by a space. the class of the query is implicitly IN (iykyk).
now, when you're measuring performance serving non-terminal zones like the root zone or TLDs, note that those servers spend most of their time giving out referral responses, not authoritative answers. so a realistic input file might be mostly queries for type A for names below, not at, the delegations present in the zone.
for example: say you're testing a server configured to be authoritative for the top-level domain fi., which has delegations for domains like helsinki.fi and turku.fi. then your input file could contain lines like
www.turku.fi A
www.helsinki.fi A
where that www prefix makes sure the server responds with a referral π. ideally you'd mix in a realistic proportion of queries for nonexistent domains alongside the existing ones, and the lines of the input file should be in a random order. shuffle that thing, bestie π²
want to use a space, or a dot, or literally any other character in the domain name? you can quote them using \DDD where each D is a digit and it's the octet corresponding to the decimal number described by DDD β as per RFC1035 5.1. Format, no cap. dots can be quoted without converting, for example:
space\032space.com A
tab\009tab.com A
dot\.dot.com A
π Constructing a dynamic update input file π
to test dynamic update performance, you run dnsperf with the -u option, and the input file gets built out of blocks of lines describing dynamic update messages. the first line in a block contains the zone name:
example.com
after that, the lines contain prerequisites β if there are any. prerequisites can say that a name may or may not exist, an rrset may or may not exist, or an rrset exists and its rdata matches all specified rdata for that name and type. the keywords require and prohibit are followed by the appropriate information. all relative names are considered to be relative to the zone name. here are the 5 types of prerequisites:
require a
require a A
require a A 1.2.3.4
prohibit x
prohibit x A
then the subsequent lines contain records to be added, records to be deleted, rrsets to be deleted, or names to be deleted. the keywords add or delete come first, followed by the appropriate information. all relative names are considered relative to the zone name (again, consistent queen π). here are the 4 types of updates:
add x 3600 A 10.1.2.3
delete y A 10.1.2.3
delete z A
delete w
and every update message gets terminated by a line containing the command:
send
that send is the little "go bestie go" at the end of each block π
β Running the tests β
when you run dnsperf, a data file (the -d option) and a server (the -s option) will normally be specified. the output of dnsperf is mostly self-explanatory, so you can just read it and vibe.
but pay attention to the number of dropped packets reported π¨ β when you're running the test over a local Ethernet connection, it should be zero. if one or more packets has been dropped, there may be a problem with the network connection. in that case, the results should be considered suspect and you should repeat the test. repeat it. do not trust those numbers, they're sus fr fr.
π Using DNS-over-HTTPS π
when you're using DNS-over-HTTPS you must set -O doh-uri=... to something that actually works with the server you're sending to. also note that the value for maximum outstanding queries will be used to control the maximum concurrent streams within the HTTP/2 connection. that's the tea β
okay that's part 1 bestie, more coming π
β¨ OPTIONS β¨
okay bestie, this is the part where dnsperf hands you the whole remote control ποΈ β every flag, every knob, every little "actually can youβ¦". Scroll with me, don't skip, they all matter.
-alocal_addr β picks the local address your requests get sent from. Leave it out and you get the wildcard address, no thoughts head empty π«§-bbufsize β sets the socket's send and receive buffer size, measured in kilobytes. If you don't say anything, your operating system's default just does its thing. π§Έ-Bβ tells dnsperf to read the datafile in TCP-stream binary format, exactly as laid out by RFC 1035 in the "4.2.2. TCP usage" section. Here's the tea: every packet is preceded by a 2-byte preamble giving the length of the DNS packet that follows, in network byte order, and then immediately the raw bytes of that packet. Those first two bytes of any packet should hold the message ID, and dnsperf overwrites them on the fly βοΈ. All the other bytes stay exactly as they are. Packets shorter than two bytes get sent intact. The packets in your datafile can be any arbitrary bytes β nobody checks them for validity. And no cap: malformed packets probably won't get responded to by servers, and that means timeouts π. This option is mutually exclusive with-D,-e,-E,-uand-y.
You can generate these binary datafiles with any old TCP listener, like netcat (nc) or sockat. TCP is the move here because that's what gets the length prepended. This example shows you how to build a datafile with two queries and then use it β you'll need two terminals:
(terminal 1) $ nc -l 127.0.0.1 5300 > dns.blob
(terminal 2) $ echo "example.com A" | dnsperf -s 127.0.0.1 -p 5300 -m tcp
(terminal 1) $ nc -l 127.0.0.1 5300 >> dns.blob
(terminal 2) $ echo "example.com AAAA" | dnsperf -s 127.0.0.1 -p 5300 -m tcp
(terminal 1) $ dnsperf -B -d dns.blob -s $IP
-cclients β go full main-character and act as multiple clients. Requests get sent from multiple sockets. Default? Just the one client, living its single-socket life. π¦-ddatafile β your input data file. Not specified? dnsperf just reads from standard input like a little gremlin. π-Dβ sets the DO (DNSSEC OK) bit [RFC3225] in every packet that gets sent. This also switches on EDNS0, which DNSSEC literally requires. Mutually exclusive with-B. π«Ά-eβ enables EDNS0 [RFC2671] by slapping an OPT record onto all the packets sent. Also mutually exclusive with-B.-Ecode:value β adds an EDNS [RFC2671] option to every packet sent, using the numeric option code you give and the value as a hex-encoded string. This also enables EDNS0. And yes, mutually exclusive with-B. π-ffamily β says which address family is used for sending DNS packets. Possible values:"inet","inet6", or"any". If"any"(which is the default) is chosen, dnsperf picks whichever address family actually fits the server it's sending to. Adaptive icon behavior β¨-hβ prints a usage statement and dips. π-jβ NOTE: Only available if built-in!
Outputs streaming JSON objects for progress and statistics. Full details live in the JSON OUTPUT section. Behold:
$ src/dnsperf -d ../src/test/datafile -s 172.17.0.1 -j -S1 -Q1 -n 2
{"start":{"version":"2.14.0","command_line":["dnsperf","-d","../src/test/datafile","-s","172.17.0.1","-j","-S1","-Q1","-n","2"],"queries":true,"server_addr":"172.17.0.1","server_port":53,"started":"Thu Dec 18 08:41:05 2025","maxruns":2}}
{"rate":{"timestamp_usec":1766047266411099,"qps":0.99872063886161822}}
{"rate":{"timestamp_usec":1766047267412462,"qps":1.997277710480615}}
{"rate":{"timestamp_usec":1766047268413677,"qps":0.99878647443356328}}
{"stop":{"reason":"end of file"}}
{"statistics":{"sent":4,"completed":4,"lost":0,"request_avg_pktsize":28,"response_avg_pktsize":50,"run_time":4.0,"qps":0.99992450569981972,"latency":{"avg":0.02145,"min":0.00026400000000000002,"max":0.042937000000000003,"stddev":0.024424912669581168}}}
It plays nicely with the extended options verbose-interval-stats and latency-histogram. Currently this option is not compatible with -v or -W though.
-llimit β sets a time limit for the run, in seconds. Heads up: this can make the input get read multiple times, or only part of it get read. Default behavior is to read the input once with no specific time limit. β³-mmode β the transport mode, bestie. Options are"udp","tcp","dot"or"doh". Default is"udp". π-nruns_through_file β run through the input file at most this many times. If there's no time limit set, the file gets read exactly this number of times. If there is a time limit, the file may get read fewer times. Math is math. π€·-pport β sets the port the DNS packets go out on. Not specified? You get the standard DNS port (udp/tcp53, DoT 853, DoH 443). π-qnum_queries β the max number of outstanding requests. Once you hit that number, dnsperf stops sending new requests until responses come in or requests time out. Default is100. π-Qmax_qps β caps the number of requests per second. There's no default limit β go feral (responsibly). π₯-sserver_addr β the name or address of the server receiving your requests. Default is the loopback address,127.0.0.1. Home sweet home. π-Sstats_interval β if you set this, dnsperf prints a count of the number of answers received per second during the interval, everystats_intervalseconds. Constant little progress updates, we love her. π-ttimeout β the request timeout value, in seconds. After this many seconds have gone by, dnsperf stops waiting for a response to that particular request. Default is5seconds. β°-Tthreads β run multiple client threads. By default dnsperf uses one thread for sending requests and one thread for receiving responses. Specify this option and it switches to using N pairs of send/receive threads instead. Teamwork makes the dream work. π―-uβ tells dnsperf to send DNS dynamic update messages instead of queries. The input file format is different for this, so go read the "Constructing a dynamic update input file" section for the deets. Mutually exclusive with-B. π-vβ verbose mode, the yapper era. The DNS RCODE of every response gets reported to standard output when the response arrives, latency included. Query times out? It's reported with the special string"T"instead of a normal DNS RCODE. Query gets interrupted? Special string"I". Extra info about network readiness and congestion gets reported too. π£οΈ-Wβ logs warnings and errors to standard output instead of standard error, so scripts, tests and automation can catch all the output in one place. Convenient king behavior. π-xlocal_port β the local port requests get sent from. Default is the wildcard port (0).
And here's the nuance: if you're acting as multiple clients and the wildcard port is used, each client gets its own different random port. But if you specify a port, the clients use a range of ports starting with the specified one. π―
-y[alg:]name:secret β adds a TSIG record [RFC2845] to all packets sent, using the TSIG key algorithm, name and secret you specify. The algorithm defaults tohmac-md5, and the secret is expressed as a base-64 encoded string. Available algorithms are:hmac-md5,hmac-sha1,hmac-sha224,hmac-sha256,hmac-sha384andhmac-sha512. Mutually exclusive with-B. π-Ooption=value β sets an extended long option for all sorts of things that control different aspects of testing or the protocol modules. The full menu of available options is in EXTENDED OPTIONS. π§
next up: more sections, keep scrolling bestie, we're not done yet π
β¨ EXTENDED OPTIONS β¨
okay so these are the extended options, and they are lowkey the extra-spicy controls, bestie. β¨
doh-uri=URIβ the URI to use for DNS-over-HTTPS. The default value is"https://localhost/dns-query". π«Άdoh-method=HTTP_METHODβ the HTTP method to use when querying with DNS-over-HTTPS. Default isGET. Available methods are:GET,POST. πtls-sni=SERVER_NAMEβ the Server Name Indication (SNI) to use for TLS connections (such as DNS-over-TLS or DNS-over-HTTPS). It defaults to leaving out the SNI extension in the client hello. πsuppress=MESSAGE[,MESSAGE,...]β suppress various messages and warnings that may be shown excessively in some situations, like socket readiness when you're connecting to a slow service. You can suppress multiple types by listing them as a comma-separated list. These types are available:timeoutsβ suppress messages about queries being timed out.congestionβ suppress messages about network congestion.sendfailedβ suppress messages about failure to send packets, or if only parts of the packet were sent.sockreadyβ suppress messages about socket readiness.unexpectedβ suppress messages about answers with an unexpected message ID.
num-queries-per-conn=NUMBERβ this limits the number of queries sent over a connection before triggering a re-connection. Once re-connected, it resets the counter and keeps sending queries until the limit is reached again, triggering another re-connection, and so on. Using this option also enables counting the number of responses received for each connection. Once the limit is reached for sending queries, it waits until the same amount of responses has been received before re-connecting. Waiting for responses may timeout, and the timeout used for this is the same as specified by-t. Note that this option is only useful for connection oriented protocols. πverbose-interval-statsβ change the statistics format of-Sto that shown at end of run. π
Please note: min/max values for latency and connections are not available in interval statistics. The number of answers received withinstats_intervalcan legitimately exceed the number of queries sent, depending on answer latency, configured timeout, andstats_interval.
Only available in dnsperf.
latency-histogramβ print detailed latency histograms for DNS answers and connections. Latency is quantized into bins with roughly 3 % resolution, and the latency range for individual bins increases logarithmically. This is done to limit the amount of memory required for histograms, and it also allows you to visualize latency using logarithmic percentile histograms with minimal postprocessing. π Only available indnsperfand if compile time support was detected.qps-threshold-wait=<microseconds>β when using-Qto rate limit queries sent, this option controls the minimum time (in microseconds) there should be between queries to usenanosleep()to wait until sending the next query. Setting it to zero (0) will disable any call tonanosleep()between sending queries. β³ If the time between queries is lower than this, then no wait is performed, in order to have more precision on when to send the next query. This is because during high QPS rate limiting, it can take more time just calling the functions to wait for when to send the next query than the actual time between queries. If not set, the average call-time tonanosleep()will be measured during startup. Only available indnsperf.tls-session-resumptionβ enable TLS session resumption for DNS-over-TLS and DNS-over-HTTPS connections. β¨ This option is only useful for connection-oriented TLS transports and is primarily intended for testing reconnect and session resumption behavior. Only available indnsperf.tls-sslkeylogfileβ enable logging of TLS session secrets to the file specified by theSSLKEYLOGFILEenvironment variable. π When enabled, TLS secrets are appended to the file pointed to bySSLKEYLOGFILEin NSS key log format. This can be used with tools such as Wireshark to decrypt captured TLS traffic for debugging purposes. IfSSLKEYLOGFILEis not set, no key log file will be written. Only available indnsperf.
π¦ JSON OUTPUT π¦
Following data is available in the JSON output, and the dot is a delimiter for objects. So like start.version, statistics.sent, etc. β iykyk. π
start.version=stringβ the version ofdnsperfthat generated the JSON.start.command_line=[string,...]β the executed command and its options as an array of strings.start.updates=booleanβ set totrueif what was sent was dynamic updates.start.queries=booleanβ set totrueif what was sent was DNS queries.start.server_addr=stringβ the server address sent to.start.server_port=intβ the server port sent to.start.started=stringβ date and time when execution started.start.timelimit=doubleβ set to a number of seconds if execution was limited to that.start.maxruns=intβ set to a number of runs if execution was limited to that.rate.timestamp_usec=uintβ current UNIX timestamp in microseconds.rate.qps=doubleβ current queries per second sent.stop.reason=stringβ the reason the execution was stopped.statistics.interval=booleanβ set totrueif this statistics object was outputted as requested by the extended optionverbose-interval-stats.statistics.sent=uintβ the number of queries sent.statistics.completed=uintβ the number of responses received for queries sent.statistics.lost=uintβ the number of queries that didn't receive a response, either timed out or by other means.statistics.interrupted=uintβ the number of queries that was in some way interrupted.statistics.unexpected=uintβ the number of responses received that did not have a corresponding query.statistics.request_avg_pktsize=uintβ average packet size for queries.statistics.response_avg_pktsize=uintβ average packet size for responses.statistics.runtime=doubleβ the run time in seconds.statistics.qps=doubleβ the queries per second.statistics.latency=latencyβ a query/response latency object, see below.statistics.connection.attempts=uintβ the number of connection attempts made. Only available when using connection oriented protocols.statistics.connection.completed=uintβ the number of connections established. Only available when using connection oriented protocols.statistics.connection.latency=latencyβ a connection latency object, see below. Only available when using connection oriented protocols.dns_over_https.status_codes={ http_status_code: count, ... }β specific statistics when DNS-over-HTTPS protocol is used. This is an object with a counter for each of the HTTP Status codes received.dns_over_https.tls_sessions_resumed=uintβ the number of TLS sessions resumed on a reconnect for DNS-over-HTTPS when usingtls-session-resumption.dns_over_tls.tls_sessions_resumed=uintβ the number of TLS sessions resumed on a reconnect for DNS-over-TLS when usingtls-session-resumption.latency.avg=doubleβ average latency.latency.min=doubleβ minimum latency.latency.max=doubleβ maximum latency.latency.stddev=doubleβ standard deviation of the latency.latency.histogram=histogramβ a histogram of the latency, see below.histogram=[[min, max, count],...]β a histogram object, see extended optionlatency-histogramfor more information.
π SEE ALSO π
π AUTHOR π
- Nominum, Inc.
- Maintained by DNS-OARC
https://www.dns-oarc.net/
π BUGS π
For issues and feature requests please use:
For question and help please use:
admin@dns-oarc.net
This whole page is giving dnsperf 2.16.0 and it's the dnsperf(1) manual page energy. Byeeee, bestie. π β¨π¦
π¦ where this page comes from
- nixpkgs unstable: dnsperf 2.16.0
- ubuntu stonking: dnsperf 2.16.0-1 Β· Apache-2.0
- Copyright 2019-2026 OARC, Inc
- Copyright 2017-2018 Akamai Technologies
- Copyright 2006-2016 Nominum, Inc
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms