My tradition is to set all zone serials to the timestamp at modification. Now that Puppet is my new religion, I want to set serial timestamps when building zone files from exported resources. A somewhat trivialized example may look like this:

file { "/tmp/dafile": content = inline_template("<%= Time.now.to_i %>"), }

The problem with this approach is that content will be different all the time, which will (ultimately) provoke rebuilding of zone files on each puppet config poll.

Is there some way I can insert a timestamp without it being included in the data that is compared against previous state?

  • What is your problem with a frequently changing serial? Why not just accept it will change frequently?
    – Zoredache
    Nov 29 '11 at 4:41
  • 1
    It consumes system resources; reassembling the zone file from all the exported resources takes tens of seconds. It generates log spam from both puppet and bind. It will require frequent reloads of bind configuration, which incurs a small risk. Also, it is impolite to invalidate the zone essentially every refresh interval unless necessary.
    – Bittrance
    Nov 29 '11 at 4:52
  • BTW, what are you using to generated your DNS zones?
    – Zoredache
    Nov 29 '11 at 5:12

Don't use a template, if you try to use a serial number there the problem is your going to keep making changes each time.

I have two ideas:

  1. Create a proper type that can manage DNS using DNS updates via the standard API. Then let BIND do the serial number increments itself.
  2. Use a file fragment pattern on each element within your DNS zone, and have it so that the main zone file only gets updated when these change. You do this by having a 'zone refresh' exec that concats your parts into the final zone including the header. The difference between most file fragment solutions would be that you generate your zone serial from a timestamp or some such, which should only ever get triggered when the parts are changed thus avoiding the constant serial number changes you would get from a template.

Some examples of the file fragment pattern are here:




How about using the timestamp of the file:

file { "/tmp/dafile": content = inline_template("<%= File.mtime("/tmp/dafile").to_i %>"), }

The only thing is that this will probably run on each client and may update the timestamp of the file for every run. If it doesn't, it should suit your requirement.


How about the following,

$utime_serial = inline_template("<%= Time.now.to_i %>")

file { "/var/named/$domain.hosts":
          content => template("named/$domain.hosts.erb"),
          owner => root,
          group => named,
          mode => 0640,

where the erb template file contains,

@             IN      SOA       galaxy.example.com.  sysadmin.example.com.  (
                               <%=utime_serial %>       ; Serial
                                8H             ; Refresh
                                2H             ; Retry
                                4W             ; Expire
                                1D )           ; Minimum

can you run external commands from within puppet (I´m using cfengine, don't know puppet) Whats about this /bin/date '+%Y%m%d00'

  • That is certainly possible and is what I am doing with the exec command (note the /bin/date in there) but you would not want to use that as a serial as if you make more then one change per day to the same zone, the serial will not get incremented.
    – Geoffrey
    Feb 3 '15 at 7:36

I used the following to perform this:

file {"${zone['zoneName']}.db":
        name            => "/var/lib/bind/.temp/${zone['zoneName']}.db",
        ensure          => file,
        content         => template('dns/bind/zone.db.erb'),
        owner           => 'root',
        group           => 'bind',
        mode            => 'ug=r,o=',
        require         => File['/var/lib/bind/.temp'],
        notify          => Exec["updateSerial-${zone['zoneName']}"]

exec {"updateSerial-${zone['zoneName']}":
        command         => "/bin/sed \"s/#SERIAL#/$(/bin/date '+%s')/\" '/var/lib/bind/.temp/${zone['zoneName']}.db' > '/var/lib/bind/${zone['zoneName']}.db'",
        refreshonly     => true,
        require         => File["${zone['zoneName']}.db"],
        notify          => Service['bind']

The template has #SERIAL# as a place holder, after the temp file has been created the Exec is notified which then uses sed and date to replace the placeholder with the current unix timestamp, finally writing the file out to the correct location.


I've tended to use the modification time of the manifest or hiera file which you are declaring the host entries in and to convert that into a suitable timestamp for the serial. (You can also use whichever is the newest from a set of files if it's split across multiple files, or a timestamp for the most recent change if it's via some other route like a database)

Unfortunately the maximum serial number is a 32-bit unsigned integer, so you can only use numbers up to 2147483647. This doesn't allow us to simply use seconds since unix epoch as the serial number, unfortunately. Instead the default format is to use YYYYMMDDxx, but this requires you to have the current serial number as state if you have set it already on the same date.

As an alternative, and one which doesn't require you to read in a file and increment the number, I use the following inline template:

$serial_mtime_file = '/etc/puppetlabs/code/environments/production/site/profile/manifests/dns_dhcp_pxe.pp'
$serial_secs = inline_template("<%= File.mtime(@serial_mtime_file).strftime(\"%y%j\").to_s + (File.mtime(@serial_mtime_file).to_i % 86400).to_s %>")

notify { "Created magical serial number ${serial_secs}": }

This gets you a YYDDDsssss format (2 digit year, 3 digit day-of-year, 5 digit second-in-day), which will work until 2099 (if you start at 2000 as I did above) and allows an update every second until then. This allows using this variable as an argument to any existing module which you want to use to create the bind configs, rather than needing a template which you can read back the existing serial (to increment) from.

So templates are OK if you get a bit creative about where you get the time from to set the serial number with :)

I have used the above with the camptocamp/bind puppetforge module and this works correctly

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.