Appendix A. Backends in detail

Table of Contents

1. PipeBackend
1.1. PipeBackend protocol
1.2. Notes
2. Random Backend
3. Generic MySQL and PgSQL backends
3.1. MySQL specifics
3.2. PostgreSQL specifics
3.3. Oracle specifics
3.4. Basic functionality
3.5. DNSSEC queries
3.6. Master/slave queries
3.7. Comments queries
3.8. Fancy records
3.9. Settings and specifying queries
3.10. Native operation
3.11. Slave operation
3.12. Superslave operation
3.13. Master operation
3.14. Disabled data
4. Oracle backend
4.1. The Database Schema
4.2. The SQL Statements
5. Generic SQLite backend (2 and 3)
5.1. Compiling the SQLite backend
5.2. Setting up the database
5.3. Using the SQLite backend
6. DB2 backend
7. Bind zone file backend
7.1. Operation
7.2. Pdns_control commands
7.3. Performance
7.4. Master/slave configuration
7.5. Commands
8. LMDB (high performance) backend
8.1. Operation
8.2. Database Format
9. ODBC backend
10. XDB Backend
11. LDAP backend
12. OpenDBX backend
13. Geo backend
14. GeoIP backend
14.1. Prerequisites
14.2. Configuration Parameters
14.3. Zonefile format
15. Lua Backend
16. TinyDNS Backend
16.1. Configuration Parameters
16.2. Location and Timestamp support
16.3. Master mode
16.4. Useful implementation notes
17. Remote Backend
17.1. Important notices
17.2. Compiling
17.3. Usage
17.4. API
17.5. Examples

This appendix lists several of the available backends in more detail

1. PipeBackend

Table A.1. PipeBackend capabilities

DNSSECPartial, no delegation, no key storage
Disabled dataNo
Module namepipe
Launch namepipe

The PipeBackend allows for easy dynamic resolution based on a 'Coprocess' which can be written in any programming language that can read a question on standard input and answer on standard output.

To configure, the following settings are available:


Command to launch as backend. Mandatory.

Or the path to a unix domain socket file. The socket should already be open and listening before pdns starts. Available since version 3.3.


Number of milliseconds to wait for an answer from the backend. If this time is ever exceeded, the backend is declared dead and a new process is spawned. Available since version 2.7.


If set, only questions matching this regular expression are even sent to the backend. This makes sure that most of PowerDNS does not slow down if you you deploy a slow backend. A query for the A record of '' would be presented to the regex as ';A'. A matching regex would be '^;.*$'.

To match only ANY and A queries for, use '^;(A|ANY)$'. Please be aware that the single quotes used in this document should not be present in the configuration file, and only on the command line. In the configuration file, the previous example would be stored as: pipe-regex=^;(A|ANY)$

Available since version 2.8.


This is the version of the question format that is sent to the co-process (pipe-command) for the pipe backend.

If not set the default pipebackend-abi-version is 1. When set to 2, the local-ip-address field is added after the remote-ip-address. (the local-ip-address refers to the IP address the question was received on). When set to 3, the real remote IP/subnet is added based on edns-subnet support (this also requires enabling 'edns-subnet-processing'). When set to 4 it sends zone name in AXFR request.

1.1. PipeBackend protocol

Questions come in over a file descriptor, by default standard input. Answers are sent out over another file descriptor, standard output by default. Questions and answers are terminated by single newline ('\n') characters.


PowerDNS sends out 'HELO\t1', indicating that it wants to speak the protocol as defined in this document, version 1. For abi-version 2 or 3, PowerDNS sends 'HELO\t2' or 'HELO\t3'. A PowerDNS Coprocess must then send out a banner, prefixed by 'OK\t', indicating it launched successfully. If it does not support the indicated version, it should respond with FAIL, but not exit. Suggested behaviour is to try and read a further line, and wait to be terminated.


Questions come in three forms and are prefixed by a tag indicating the type:


Regular queries


List requests, which mean that an entire zone should be listed


Check if the coprocess is functioning

The question format, for type Q questions:

pipebackend-abi-version = 1 [default]

Q	qname		qclass	qtype	id	remote-ip-address

pipebackend-abi-version = 2

Q	qname		qclass	qtype	id	remote-ip-address	local-ip-address

pipebackend-abi-version = 3

Q	qname		qclass	qtype	id	remote-ip-address	local-ip-address	edns-subnet-address

Fields are tab separated, and terminated with a single \n. The remote-ip-address is the IP address of the nameserver asking the question; the local-ip-address is the IP address on which the question was received.

Type is the tag above, qname is the domain the question is about. qclass is always 'IN' currently, denoting an INternet question. qtype is the kind of information desired, the record type, like A, CNAME or AAAA. id can be specified to help your backend find an answer if the id is already known from an earlier query. You can ignore it unless you want to support AXFR.

remote-ip-address is the ip-address of the nameserver asking the question. local-ip-address is the ip-address that was queried locally. edns-subnet-address is the actual client subnet as provided via edns-subnet support. Note that for the SOA query that precedes an AXFR, edns-subnet is always set to


Queries for wildcard names should be answered literally, without expansion. So, if a backend gets a question for "*", it should only answer with data if there is an actual "*" name

AXFR-queries look like this:

AXFR	id	zoneName

The id is gathered from the answer to a SOA query. ZoneName is given in ABI version 4.


Each answer starts with a tag, possibly followed by a TAB and more data.


Indicating a successful line of DATA.


Indicating the end of an answer - no further data.


Indicating a lookup failure. Also serves as 'END'. No further data.


For specifying things that should be logged. Can only be sent after a query and before an END line. After the tab, the message to be logged.

So, letting it be known that there is no data consists of sending 'END' without anything else. The answer format (for abi-version 1 and 2):

DATA	qname		qclass	qtype	ttl	id	content	

'content' is as specified in Chapter 23, Supported record types and their storage. For MX and SRV, content consists of the priority, followed by a tab, followed by the actual content.

A sample dialogue may look like this (note that in reality, almost all queries will actually be for the ANY qtype):

Q	IN	A	-1
DATA	IN	A	3600	1
DATA	IN	A	3600	1
DATA	IN	A	3600	1

This would correspond to a remote webserver wanting to resolve the IP address of, and PowerDNS traversing the CNAMEs to find the IP addresses of Another dialogue might be:

Q		IN	SOA	-1
DATA		IN	SOA	86400	1 ...
DATA		IN	SOA	86400	1 ...
DATA		IN	NS	86400	1
DATA		IN	NS	86400	1
DATA	IN	A	86400	1
DATA	IN	A	86400	1

This is a typical zone transfer.

For abi-version 3, DATA-responses get two extra fields:

DATA	scopebits	auth	qname		qclass	qtype	ttl	id	content	

scopebits indicates how many bits from the subnet provided in the question (originally from edns-subnet) were used in determining this answer. This can aid caching (although PowerDNS does not currently use this value). The auth field indicates whether this response is authoritative; this is for DNSSEC. In the auth field, use 0 for non-authoritative or 1 for authoritative.

For api-versions 1 and 2, the two new fields fall back to default values. The default value for scopebits is 0. The default for auth is 1 (meaning authoritative).

Sample perl backend

#!/usr/bin/perl -w
# sample PowerDNS Coprocess backend

use strict;

$|=1;					# no buffering

my $line=<>;

unless($line eq "HELO\t1") {
	print "FAIL\n";
	print STDERR "Received '$line'\n";
print "OK	Sample backend firing up\n";	# print our banner

	print STDERR "$$ Received: $_";
	my @arr=split(/\t/);
	if(@arr<6) {
		print "LOG	PowerDNS sent unparseable line\n";
		print "FAIL\n";

	my ($type,$qname,$qclass,$qtype,$id,$ip)=split(/\t/);

	if(($qtype eq "SOA" || $qtype eq "ANY") && $qname eq "") {
		print STDERR "$$ Sent SOA records\n";
		print "DATA	$qname	$qclass	SOA	3600	-1 2008080300 1800 3600 604800 3600\n";
	if(($qtype eq "NS" || $qtype eq "ANY") && $qname eq "") {
		print STDERR "$$ Sent NS records\n";
		print "DATA	$qname	$qclass	NS	3600	-1\n";
		print "DATA	$qname	$qclass	NS	3600	-1\n";
	if(($qtype eq "TXT" || $qtype eq "ANY") && $qname eq "") {
		print STDERR "$$ Sent NS records\n";
		print "DATA	$qname	$qclass	TXT	3600	-1	\"hallo allemaal!\"\n";
	if(($qtype eq "A" || $qtype eq "ANY") && $qname eq "") {
		print STDERR "$$ Sent A records\n";
		print "DATA	$qname	$qclass	A	3600	-1\n";
		print "DATA	$qname	$qclass	A	3600	-1\n";
		print "DATA	$qname	$qclass	A	3600	-1\n";
	elsif(($qtype eq "CNAME" || $qtype eq "ANY") && $qname eq "") {
		print STDERR "$$ Sent CNAME records\n";
		print "DATA	$qname	$qclass	CNAME	3600	-1\n";
	elsif($qtype eq "MBOXFW") {
		print STDERR "$$ Sent MBOXFW records\n";
		print "DATA	$qname	$qclass	MBOXFW	3600	-1	powerdns\\n";

	print STDERR "$$ End of data\n";
	print "END\n";

1.2. Notes

Besides regular query types, the DNS also knows the 'ANY' query type. When a server receives a question for this ANY type, it should reply with all record types available.

Backends should therefore implement being able to answer 'ANY' queries in this way, and supply all record types they have when they receive such an 'ANY' query. This is reflected in the sample script above, which for every qtype answers if the type matches, or if the query is for 'ANY'.

However, since backends need to implement the ANY query anyhow, PowerDNS makes use of this. Since almost all DNS queries internally need to be translated first into a CNAME query and then into the actual query, possibly followed by a SOA or NS query (this is how DNS works internally), it makes sense for PowerDNS to speed this up, and just ask the ANY query of a backend.

When it has done so, it gets the data about SOA, CNAME and NS records in one go. This speeds things up tremendously.

The upshot of the above is that for any backend, including the PIPE backend, implementing the ANY query is NOT optional. And in fact, a backend may see almost exclusively ANY queries. This is not a bug.