OpenNTP ships a single Docker container that runs Chrony (NTP) and a Next.js UI. Every API response is NTP-backed with a fallback to Google NTP if the local Chrony server is unavailable.
- Chrony configuration in chrony/
- Next.js UI with multi-timezone digital clocks
- NTP-backed REST APIs and Swagger docs
Build and run the OpenNTP service:
docker compose up -d --buildDefault ports:
- UDP/123 for NTP
- HTTP/3000 for the UI and APIs
OpenNTP uses Chrony inside the container. To make it your own:
- Edit the upstream sources and access rules in chrony/chrony.conf.
- If you want a private server, remove the UDP/123 mapping or restrict it with a firewall.
- Start the stack with
docker compose up -d --build. - Point your clients to your host on UDP/123.
Example client check:
ntpdate -q <your-server-ip>Frontend (UI only):
NEXT_PUBLIC_NTP_EXPOSED=true|false
NEXT_PUBLIC_NTP_HOST=ntp.example.com
NEXT_PUBLIC_TIMEZONES=Bangladesh|Asia/Dhaka, New York|America/New_YorkNTP sources:
NTP_SERVER_HOST=127.0.0.1
NTP_FALLBACK_HOST=time.google.com
NTP_TIMEOUT_MS=2500The UI flags only change the display. Exposure is still controlled by Docker Compose and your firewall rules.
GET /api/time- NTP-backed time payloadGET /api/timezones- every Moment timezone with NTP timeGET /api/health- uptime, Chrony tracking, NTP metadataGET /api/openapi- OpenAPI specGET /docs- Swagger UI
Example response for GET /api/time:
{
"nowIso": "2026-02-08T18:04:01.528Z",
"unix": 1770573841,
"timezone": "America/New_York",
"offsetMinutes": -300,
"source": "ntp",
"ntp": {
"host": "127.0.0.1",
"source": "local",
"warning": null
}
}pnpm install
pnpm run devOpen http://localhost:3000.
