COREDNS-VIEW(7)

๐Ÿ“ฆ
version 1.15.0 ยท 3 versions
, in opensuse-leap 16.0

๐Ÿ’… COREDNS-VIEW(7) โ€” the view plugin, explained by your favorite Unix bestie

okay so bestie, buckle up, because this is the plugin manual page for view โ€” and no, it's not the thing you do on TikTok, it's the thing that decides which server block gets your DNS request. iconic behavior, honestly.


โœจ NAME โœจ

view โ€” defines the conditions that must be met for a DNS request to be routed to the server block. that's it, that's the whole vibe check. ๐ŸŽ€


๐ŸŒธ DESCRIPTION ๐ŸŒธ

so like, view defines an expression that has to evaluate to true for a DNS request to get routed to the server block. literally, if the expression isn't feeling it, the request goes elsewhere. this unlocks some highkey advanced server block routing situations โ€” the big one being split DNS. yes queen, different answers for different people. ๐Ÿ’…


๐Ÿฆ‹ SYNTAX ๐Ÿฆ‹

here's the shape of it, verbatim, don't @ me:

       view NAME {
         expr EXPRESSION
       }
  • view NAME โ€” the name of the view, used by metrics and exported as metadata for requests that match the view's expression. give it a cute name, it's basically the view's government name. ๐Ÿ“›
  • expr EXPRESSION โ€” CoreDNS will only route incoming queries to the enclosing server block if EXPRESSION evaluates to true. peep the Expressions section for all the available variables and functions. and plot twist: if you define multiple instances of view, then all of the EXPRESSIONs must evaluate to true for CoreDNS to route incoming queries to the enclosing server block. it's giving "group project where everyone has to show up." ๐Ÿ˜ญ

for expression syntax and examples, keep scrolling to the Expressions and Examples sections. ๐Ÿซถ


๐Ÿ‘‘ SERVER BLOCK ORDERING ๐Ÿ‘‘

this part matters, so pay attention, bestie:

Server blocks sharing the same zone and port are evaluated top to bottom. The first block whose view expression matches (or that has no view at all) handles the query.

โš ๏ธ An unfiltered catch-all block declared before a filtered block will shadow it, because the catch-all matches every query. like, it will simply eat its lunch. no notes. ๐Ÿ’€

to get the expected split-DNS behavior, declare all filtered (view) blocks first and the unfiltered catch-all block last. order is the whole entire point here, periodt.


๐Ÿ’– EXAMPLES ๐Ÿ’–

๐Ÿ“ CIDR-based split DNS routing

Implement CIDR based split DNS routing. This will return a different answer for test. depending on the client's IP address. It returns:

  • ... test. 3600 IN A 1.1.1.1, for queries with a source address in 127.0.0.0/24
  • test. 3600 IN A 2.2.2.2, for queries with a source address in 192.168.0.0/16
  • test. 3600 IN AAAA 2001:0DB8::1, for queries with a source address in 2001:0DB8::/32
  • test. 3600 IN A 3.3.3.3, for all others
. {
  view example1 {
    expr incidr(client_ip(), '127.0.0.0/24')
  }
  hosts {
    1.1.1.1 test
  }
}

. {
  view example2 {
    expr incidr(client_ip(), '192.168.0.0/16')
  }
  hosts {
    2.2.2.2 test
  }
}

. {
  view v6_example1 {
    expr incidr(client_ip(), '2001:0DB8::/32')
  }
  hosts {
    2001:0DB8::1 test
  }
}

}

. {
  hosts {
    3.3.3.3 test
  }
}

(yeah, there's a stray } in there โ€” that's literally how it's printed in the original, so it stays. not my business, not my problem. ๐Ÿ™ˆ)

โญ send all A and AAAA somewhere else

Send all A and AAAA requests to 10.0.0.6, and all other requests to 10.0.0.1.

. {
  view example {
    expr type() in ['A', 'AAAA']
  }
  forward . 10.0.0.6
}

. {
  forward . 10.0.0.1
}

๐Ÿงš regex name matching, escaped like a boss

Send all requests for abc.*.example.com (where * can be any number of labels), to 10.0.0.2, and all other requests to 10.0.0.1.

๐Ÿ“Œ Note that the regex pattern is enclosed in single quotes, and backslashes are escaped with backslashes. yes, double backslashes. it's giving "escape the escape." ๐Ÿ”ฅ
. {
  view example {
    expr name() matches '^abc\\..*\\.example\\.com\\.$'
  }
  forward . 10.0.0.2
}

. {
  forward . 10.0.0.1
}

๐ŸŒˆ EXPRESSIONS ๐ŸŒˆ

To evaluate expressions, view uses the expr-lang/expr package ( https://github.com/expr-lang/expr ). slay, we love a dependency with a good README.

For example, an expression could look like:

(type() == 'A' && name() == 'example.com.') || client_ip() == '1.2.3.4'

All expressions should be written to evaluate to a boolean value. true or false, no in-between, no "it depends," no situationships. ๐Ÿ’€

See https://github.com/expr-lang/expr/blob/master/docs/language-definition.md as a detailed reference for valid syntax. bookmarked, obviously.

๐Ÿ”ฎ AVAILABLE EXPRESSION FUNCTIONS ๐Ÿ”ฎ

In the context of the view plugin, expressions can reference DNS query information by using the utility functions defined below. so many little helpers, we're obsessed. ๐Ÿซถ

๐ŸŽ€ DNS QUERY FUNCTIONS ๐ŸŽ€

  • bufsize() int: the EDNS0 buffer size advertised in the query
  • class() string: class of the request (IN, CH, ...)
  • client_ip() string: client's IP address; for IPv6 addresses these are enclosed in brackets: [::1]
  • do() bool: the EDNS0 DO (DNSSEC OK) bit set in the query
  • id() int: query ID
  • name() string: name of the request (the domain name requested ending with a dot): example.com.
  • opcode() int: query OPCODE
  • port() string: client's port
  • proto() string: protocol used (tcp or udp)
  • server_ip() string: server's IP address; for IPv6 addresses these are enclosed in brackets: [::1]
  • server_port() string : server's port
  • size() int: request size in bytes
  • type() string: type of the request (A, AAAA, TXT, ...)

๐Ÿง UTILITY FUNCTIONS ๐Ÿง

  • incidr(ip string, cidr string) bool: returns true if ip is within cidr
  • metadata(label string) โ€” returns the value for the metadata matching label

๐Ÿ’œ METADATA ๐Ÿ’œ

The view plugin will publish the following metadata, if the metadata plugin is also enabled. teamwork makes the dream work, etc. โœจ

  • view/name: the name of the view handling the current request

okay that's the whole page, bestie. you're officially a view plugin girlie now. go forth and split that DNS. ๐Ÿ‘‘๐Ÿ’…

CoreDNS โ€” October 2026 edition. mwah, drink some water, and remember: filtered blocks first, catch-all last. ๐Ÿซถ๐ŸŽ€

๐Ÿ“ฆ where this page comes from