Skip to content

Add optional GPS reference clock via gpsd#22

Open
tillo wants to merge 1 commit into
simonrupf:masterfrom
tillo:add-optional-gps-support
Open

Add optional GPS reference clock via gpsd#22
tillo wants to merge 1 commit into
simonrupf:masterfrom
tillo:add-optional-gps-support

Conversation

@tillo
Copy link
Copy Markdown

@tillo tillo commented May 23, 2026

Adds optional GPS reference-clock support, mirroring the existing PTP0 opt-in pattern.

Motivation

Several of us run a stratum-1 NTP-Pool / OpenNIC server backed by a serial GPS receiver (USB UART GPS dongle, hat-mounted GPS on a router/SBC, etc.). The image already has all the chrony machinery for a serial refclock, so the only missing piece is bringing up gpsd against the device and adding a refclock SHM ... line to chrony.conf. That's what this PR does — about ten lines in the entrypoint.

I've been maintaining it as an out-of-tree fork for a year; upstreaming so anyone else with a serial GPS can run a stratum-1 from this image without a custom build.

Behaviour

Opt-in via a single env var, same shape as ENABLE_NTS/ENABLE_SYSCLK/the PTP0 device-passthrough:

devices:
  - /dev/ttyUSB0:/dev/ttyUSB0
environment:
  - GPS_DEVICE=/dev/ttyUSB0
  - GPS_BAUD=9600              # default 9600
  - GPS_REFCLOCK_OFFSET=0.0    # default 0.0; serial-latency compensation
  - GPS_PPS=false              # set true to also consume PPS via SHM seg 1

When GPS_DEVICE is set and the device exists, the entrypoint launches
gpsd -N -F /run/chrony/gpsd.sock -s ${GPS_BAUD} ${GPS_DEVICE} in background and appends refclock SHM 0 refid GPS offset … precision 1e-1 to chrony.conf. With GPS_PPS=true a second refclock SHM 1 refid PPS precision 1e-7 prefer line is added — useful for receivers whose PPS pin is wired through and gpsd is configured to publish PPS samples on SHM segment 1.

When unset, gpsd is installed but never started.

Image-size impact for non-GPS users

gpsd is added to the apk add line so the image grows by a few MB regardless of whether GPS is enabled. I considered making it a separate build target / Dockerfile.gps so the default image stays unchanged, but the unified pattern (ENABLE_NTS, ENABLE_SYSCLK, PTP0 device-passthrough — all single-image, runtime-opt-in) seemed more in line with how the rest of the image works. Happy to split into a gpsd-tagged variant instead if you prefer.

Permissions note

The serial device must be readable by the chrony user. Documented in the README — easiest is chmod g+r /dev/ttyUSB0 + chown :dialout /dev/ttyUSB0 on the host plus group_add: [dialout] on the container; or run the container as root.

Tested

In production on a Mediatek MT7988 (BPI-R4) + USB-UART GPS, feeding an NTP-Pool stratum-1 server. The fork has been running this code path since April; the recent improvement in this PR (the [ -e "${GPS_DEVICE}" ] guard) is to make a missing/unplugged GPS not abort chronyd so the container still serves stratum-2 from NTP_SERVERS until the hardware is replaced.

Mirrors the existing PTP0 opt-in pattern. When `GPS_DEVICE` is set and
the device exists, the entrypoint spawns gpsd in background pointed at
that serial UART and appends a `refclock SHM 0 refid GPS` line to
chrony.conf. Optional `GPS_BAUD`, `GPS_REFCLOCK_OFFSET` knobs, and
`GPS_PPS=true` to also consume PPS samples via SHM segment 1.

When unset, gpsd is installed but never started — non-GPS users see only
the Alpine package added to the image (a few MB), no runtime effect.

Used in production on a Mediatek MT7988 + USB-UART GPS to serve an
NTP-Pool stratum-1 from the same chronyd image. The pattern was previously
maintained as an out-of-tree fork; upstreaming so other operators with a
serial GPS can run a stratum-1 from this image without a custom build.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant