#8554·coredns

plugin/file: reload_by_mtime reloads unchanged zone files on every interval

Author: MrSnakeDocCreated Sep 15, 2026Updated Sep 15, 2026
Labelsbug

What happened:

When reload_by_mtime is enabled in the file plugin, the zone is reloaded on every reload interval even when the zone file has not been modified.

For example, with a 10-second reload interval, CoreDNS logs a successful zone reload every 10 seconds while the zone file mtime remains unchanged.

This behavior was reproduced on multiple CoreDNS instances.

What you expected to happen:

With reload_by_mtime enabled, the zone should only be reloaded when the zone file mtime is newer than the mtime of the version currently loaded.

If the file is unchanged, polling should not trigger a zone reload.

How to reproduce it (as minimally and precisely as possible):

  1. Configure a zone with reload_by_mtime and a short reload interval:

    file /etc/coredns/db.example example.test {
        reload 10s
        reload_by_mtime
    }
  2. Start CoreDNS and leave the zone file unchanged.

  3. Observe that CoreDNS reports a successful zone reload every 10 seconds:

    Successfully reloaded zone "example.test." in "/etc/coredns/db.example" with <serial> SOA serial
  4. Verify independently that the zone file mtime does not change.

  5. Remove reload_by_mtime while keeping reload 10s and restart CoreDNS.

  6. With an unchanged SOA serial, the repeated successful reloads no longer occur.

The same behavior also occurs without an explicit reload duration, in which case the unnecessary reloads follow the default reload interval.

Anything else we need to know?:

Looking at the current implementation, file_mtime appears not to be initialized by the normal Corefile configuration path.

Parse() creates a new Zone:

go
z := NewZone(origin, fileName)

if z.ReloadByMtime {
    fi, err := os.Stat(fileName)
    if err != nil {
        return nil, fmt.Errorf("failed to stat file %q with error %v", fileName, err)
    }
    z.file_mtime = fi.ModTime()
}

However, a newly created Zone has ReloadByMtime == false.

In plugin/file/setup.go (line 71), ReloadByMtime is only assigned after the zone has already been parsed:

go
z[origins[i]].ReloadByMtime = reload_by_mtime

This appears to leave file_mtime at its zero value.

The reload loop subsequently compares the actual file mtime against that value in file file/reload.go (line 31):

go
if !fi.ModTime().After(z.file_mtime) {
    continue
}

Since a real file mtime is after the zero value of time.Time, the condition to reload remains true.

Additionally, after a successful mtime-triggered reload, Reload() in file file/reload.go (line 52) replaces the zone data with:

go
z.setData(zone.Apex, zone.Tree)

but does not appear to update z.file_mtime. This may cause repeated reloads after a legitimate file modification as well.

The current reload_by_mtime tests manually initialize file_mtime in prepareMtimeZone():

go
z.ReloadByMtime = true
z.file_mtime = fi.ModTime()

so they do not exercise the same initialization path as the Corefile configuration.

Environment:

  • the version of CoreDNS: CoreDNS-1.14.7

  • Corefile:

    file /etc/coredns/db.example example.test {
        reload 10s
        reload_by_mtime
    }
  • logs, if applicable:

With a 10-second reload interval, successful zone reloads occur once per interval while the zone file mtime remains unchanged:

08:45:35.509 Successfully reloaded zone "example.test." in "/etc/coredns/db.example" with <serial> SOA serial
08:45:45.509 Successfully reloaded zone "example.test." in "/etc/coredns/db.example" with <serial> SOA serial
08:45:55.509 Successfully reloaded zone "example.test." in "/etc/coredns/db.example" with <serial> SOA serial
08:46:05.509 Successfully reloaded zone "example.test." in "/etc/coredns/db.example" with <serial> SOA serial
08:46:15.509 Successfully reloaded zone "example.test." in "/etc/coredns/db.example" with <serial> SOA serial
08:46:25.509 Successfully reloaded zone "example.test." in "/etc/coredns/db.example" with <serial> SOA serial

The same behavior is reproducible without explicitly setting reload 10s. With only reload_by_mtime configured, the zone is repeatedly reloaded using the default 1-minute reload interval, while the zone file mtime remains unchanged.

  • OS: Linux, running CoreDNS in Docker

  • Others: linux/amd64, go1.26.6