headscale icon indicating copy to clipboard operation
headscale copied to clipboard

[Bug] Headscale reads extra DNS records from path too early

Open nblock opened this issue 3 months ago • 2 comments

Is this a support request?

  • [x] This is not a support request

Is there an existing issue for this?

  • [x] I have searched the existing issues

Current Behavior

I use the dns.extra_records_path configuration option to load DNS records from a JSON file. This file is created by a script that combines multiple files into one. The relevant line is:

jq --sort-keys --slurp 'unique | flatten' dns.d/*.json > /var/lib/headscale/extra-records.json

There are about 150 DNS records written to /var/lib/headscale/extra-records.json with each invocation. From time to time Headscale's filewatcher picks up the file while it is being written. Presumably, the output buffer is flushed by jq and a partial JSON file is persisted for a short while. This causes errors about broken JSON in Headscale's logs:

[snipped]
{\\n    \\\"name\\\": \\\"acgmw.example.com\\\",\\n    \\\"type\\\": \\\"A\\\",\\n    \\\"value\\\": \\\"100.64.0.1\\\"\\n  },\\n  {\\n    \\\"name\\\": \\\"acgmx.example.com\\\",\\n    \\\"type\\\": \\\"A\\\",\\n    \\\"value\\\": \\\"100.64.0.1\\\"\\n  },\\n  {\\n    \\\"name\\\": \\\"acgmy.example.com\\\",\\n    \\\"type\\\": \\\"A\\\",\\n    \\\"v\": 
unexpected end of JSON input"

As a workaround, I use stdbuf to enlarge the output buffer for jq:

stdbuf -o 1M jq --sort-keys --slurp 'unique | flatten' dns.d/*.json > /var/lib/headscale/extra-records.json

Is there a way to configure the filewatcher such that it picks up the file once no other process is writing to it?

Expected Behavior

Unsure what is the best solution to this, some ideas:

  • Read the records in dns.extra_records_path once no other process writes to it
  • Delay the read a bit (probably just a workaround)

Steps To Reproduce

  1. Setup Headscale 0.26.1 with dns.extra_records_path
  2. Create some sample JSON files
  3. Update the JSON file with the DNS records:
    jq --sort-keys --slurp 'unique | flatten' sample-*.json > .headscale/extra-records.json
    
  4. Observe the error message in Headscale's log

Environment

- OS: Debian 13
- Headscale version: 0.26.1
- Tailscale version: -

Runtime environment

  • [ ] Headscale is behind a (reverse) proxy
  • [ ] Headscale runs in a container

Debug information

nblock avatar Aug 30 '25 11:08 nblock

It would probably be safer to first generate a temporary file, and then replace the existing one with the new one, in one go (using mv, which should be atomic).

reinob avatar Sep 18 '25 12:09 reinob

(using mv, which should be atomic).

I saw some errors with mv also; but can't remember if they were identical.

nblock avatar Sep 18 '25 13:09 nblock