# How ntpServerConverter.py Converts YAML to Markdown, chrony.conf, and ntp.toml

> Learn how ntpServerConverter.py converts YAML to Markdown, chrony.conf, and ntp.toml. Generate documentation and configuration files efficiently with this Python script.

- Repository: [Jauder Ho/nts-servers](https://github.com/jauderho/nts-servers)
- Tags: how-to-guide
- Published: 2026-03-04

---

**The [`ntpServerConverter.py`](https://github.com/jauderho/nts-servers/blob/main/ntpServerConverter.py) script reads [`nts-servers.yml`](https://github.com/jauderho/nts-servers/blob/main/nts-servers.yml) and generates three distinct output formats—Markdown tables for documentation, [`chrony.conf`](https://github.com/jauderho/nts-servers/blob/main/chrony.conf) for the Chrony NTP daemon, and [`ntp.toml`](https://github.com/jauderho/nts-servers/blob/main/ntp.toml) for ntpd-rs—using dedicated generator functions that parse hostname brackets and group servers by location.**

The [`ntpServerConverter.py`](https://github.com/jauderho/nts-servers/blob/main/ntpServerConverter.py) utility in the `jauderho/nts-servers` repository serves as a single-source-of-truth converter that transforms structured YAML data into multiple configuration and documentation formats. This Python script ensures consistency between the human-readable server list in the repository's README and the actual daemon configurations deployed in production environments.

## Loading and Parsing the YAML Source

### Reading nts-servers.yml with load_yaml()

The conversion process begins by ingesting the structured data file. The `load_yaml()` function at lines 9-12 of [`scripts/ntpServerConverter.py`](https://github.com/jauderho/nts-servers/blob/main/scripts/ntpServerConverter.py) handles this operation:

```python
def load_yaml(file_path):
    with open(file_path, "r") as file:
        return yaml.safe_load(file)

```

This helper reads the input file and returns a Python dictionary containing the server definitions, including hostnames, stratum levels, locations, and ownership metadata.

### Extracting Clean Hostnames with extract_hostname()

NTS servers in the YAML file may be written in bracketed notation such as `hostname[IP]`. The `extract_hostname()` function at lines 14-18 uses regular expressions to strip the bracketed portion so that Chrony and TOML configurations receive plain hostnames:

```python
def extract_hostname(hostname_field):
    match = re.search(r"\[([^\]]+)\]", hostname_field)
    if match:
        return match.group(1)
    return hostname_field

```

This ensures compatibility with daemon configuration formats that expect DNS names rather than bracketed IP notation.

## Generating Markdown Documentation

The `generate_markdown()` function (lines 21-56) transforms the YAML data into a human-readable table suitable for the repository's README. This generator groups servers by geographic location and handles virtual-machine entries separately.

The function constructs a Markdown table with columns for provider, hostname, port, and key details. Entries marked with `"vm": true` in the YAML are placed in a distinct "Virtual Machine" section, as virtualized servers may exhibit lower time accuracy than bare-metal implementations.

When run in default mode, the script automatically replaces the existing "## The List" section in [`README.md`](https://github.com/jauderho/nts-servers/blob/main/README.md) with the newly generated table, ensuring documentation stays synchronized with the YAML source.

## Creating chrony.conf Configuration

The `generate_chrony()` function at lines 59-84 produces configuration syntax for the Chrony NTP daemon. This generator iterates over `data["servers"]` and outputs `server <hostname> nts iburst` lines, prefixed with comment headings for each location.

The **iburst** option enables rapid initial synchronization, while **nts** enforces Network Time Security authentication. The function maintains the same location-based grouping as the Markdown generator, inserting blank lines and comments to create a readable, organized configuration file.

## Building ntp.toml for ntpd-rs

For the Rust-based `ntpd-rs` daemon, the `generate_toml()` function (lines 86-113) emits TOML `[[source]]` blocks. Each server entry becomes a separate source configuration with parameters for mode (NTS), address, and certificate verification settings.

The generator preserves location grouping through TOML comments and handles the virtual-machine segregation logic consistent with the other output formats. This ensures administrators can maintain identical server lists across Chrony and ntpd-rs deployments from a single YAML definition.

## Command-Line Interface and Usage Modes

The script uses `argparse` (lines 30-98) to support four distinct operational modes:

**Default Mode (No Arguments)**
Updates [`README.md`](https://github.com/jauderho/nts-servers/blob/main/README.md) and writes both [`chrony.conf`](https://github.com/jauderho/nts-servers/blob/main/chrony.conf) and [`ntp.toml`](https://github.com/jauderho/nts-servers/blob/main/ntp.toml):

```bash
python3 scripts/ntpServerConverter.py data/nts-servers.yml

```

**Single Format Modes**
Generate only one specific output:

```bash

# Markdown only

python3 scripts/ntpServerConverter.py data/nts-servers.yml markdown docs/servers.md

# Chrony configuration only

python3 scripts/ntpServerConverter.py data/nts-servers.yml chrony chrony.conf

# TOML configuration only

python3 scripts/ntpServerConverter.py data/nts-servers.yml toml ntp.toml

```

The `main()` function (lines 45-99) orchestrates the workflow: loading YAML, selecting the appropriate generator based on CLI arguments, and writing output to the specified destination.

## Summary

- **ntpServerConverter.py** serves as the central conversion engine for the `jauderho/nts-servers` repository, transforming structured YAML into three distinct output formats.
- The script uses **`load_yaml()`** and **`extract_hostname()`** to parse input data and normalize hostname formats before generation.
- Three dedicated generators—**`generate_markdown()`**, **`generate_chrony()`**, and **`generate_toml()`**—handle format-specific syntax, location grouping, and virtual-machine segregation.
- The CLI supports both **default mode** (updating README and writing all configs) and **single-format mode** for targeted output generation.
- All file paths, function names, and code examples reference the actual implementation in [`scripts/ntpServerConverter.py`](https://github.com/jauderho/nts-servers/blob/main/scripts/ntpServerConverter.py).

## Frequently Asked Questions

### How does ntpServerConverter.py handle bracketed hostnames in the YAML file?

The script uses the `extract_hostname()` function with a regular expression pattern `r"\[([^\]]+)\]"` to detect and remove bracketed IP addresses from hostname fields. If brackets are found, the function returns only the content inside them; otherwise, it returns the original hostname unchanged. This ensures Chrony and TOML configurations receive clean DNS names compatible with NTP daemon requirements.

### Can I use ntpServerConverter.py to generate only a Markdown table without updating the README?

Yes. Run the script with the `markdown` argument followed by your desired output file path: `python3 scripts/ntpServerConverter.py data/nts-servers.yml markdown output.md`. This single-format mode bypasses the README update logic and writes only the Markdown table to the specified destination, making it suitable for generating documentation fragments for external use.

### What is the difference between the default mode and single-format mode in ntpServerConverter.py?

Default mode runs when you omit the output format argument, automatically updating the `## The List` section in [`README.md`](https://github.com/jauderho/nts-servers/blob/main/README.md) and writing both [`chrony.conf`](https://github.com/jauderho/nts-servers/blob/main/chrony.conf) and [`ntp.toml`](https://github.com/jauderho/nts-servers/blob/main/ntp.toml) files. Single-format mode requires specifying either `markdown`, `chrony`, or `toml` as the second argument, which causes the script to generate only that specific format and write it to the user-provided path, without modifying the README or creating other config files.

### How does ntpServerConverter.py separate virtual machine servers from bare-metal servers?

The script checks the `"vm": true` boolean field in each YAML entry. When `generate_markdown()`, `generate_chrony()`, or `generate_toml()` encounter this flag, they place those servers in a separate "Virtual Machine" section or grouping. This segregation ensures users can distinguish between potentially less accurate virtualized time sources and bare-metal NTS servers when configuring their NTP clients.