A simple lightweight NTP server written in Node.js using UDP sockets.
This project implements a basic NTP server that responds to standard NTP client requests and returns the current system time in NTP timestamp format.
Repository: https://github.com/TheGreatAzizi/NTP-JS-Server
- Lightweight UDP-based NTP server
- Supports standard NTP client mode requests
- Generates valid 48-byte NTP response packets
- Uses the host machine system clock as the time source
- Configurable host and port using environment variables
- Basic error handling for permission and port conflicts
- No external runtime dependencies
- Docker and Docker Compose support
- GitHub Actions CI workflow included
- Node.js 18 or newer recommended
- Administrator/root privileges if binding to UDP port
123 - Docker, optional, if you want to run the server in a container
Clone the repository:
git clone https://github.com/TheGreatAzizi/NTP-JS-Server.git
cd NTP-JS-ServerInstall dependencies:
npm installThis project currently does not require external npm packages, but running npm install keeps the workflow consistent if dependencies are added later.
Start the server:
npm startOr run the script directly:
node ntp-server.jsBy default, the server listens on:
0.0.0.0:123/udpLinux/macOS:
NTP_HOST=127.0.0.1 NTP_PORT=12345 npm startWindows PowerShell:
$env:NTP_HOST="127.0.0.1"
$env:NTP_PORT="12345"
npm start| Environment Variable | Default | Description |
|---|---|---|
NTP_HOST |
0.0.0.0 |
Host address used by the UDP server |
NTP_PORT |
123 |
UDP port used by the NTP server |
See .env.example for an example configuration file.
Note: the current script reads directly from environment variables. If you want automatic
.envloading, add a package such asdotenvor pass environment variables through your shell, Docker, or process manager.
Build the Docker image:
docker build -t ntp-js-server .Run the container:
docker run --rm -p 123:123/udp ntp-js-serverOr use Docker Compose:
docker compose up --buildIf port 123 is already in use on your machine, change the published port in docker-compose.yml or run the server with a different NTP_PORT.
You can test the server with an NTP client.
Linux/macOS using ntpdate:
ntpdate -q 127.0.0.1Using chrony:
chronyd -Q "server 127.0.0.1 iburst"If you run the server on a non-standard port, use a client or custom script that supports custom NTP ports.
The server listens for UDP packets with a minimum size of 48 bytes. When it receives a valid NTP client mode request, it creates a 48-byte NTP response and fills the required timestamp fields:
- Reference Timestamp
- Originate Timestamp
- Receive Timestamp
- Transmit Timestamp
The server converts Unix time to NTP time using the NTP epoch offset and writes timestamps in big-endian format.
This project is intended for educational, testing, lab, and local network use.
Be careful before exposing an NTP server publicly on the internet. NTP uses UDP and public NTP servers can be abused if they are not configured safely. Before running this as a public service, add proper monitoring, rate limiting, firewall rules, abuse protection, and time synchronization checks.
For production-grade public NTP services, consider mature and well-tested software such as:
- chrony
- ntpd
- ntpsec
- Uses the local system clock only
- Does not sync directly with upstream NTP servers
- Does not verify whether the host clock is synchronized
- No authentication support
- No built-in rate limiting yet
- No detailed request logging yet
- No metrics or health endpoint yet
- No automated unit tests yet
These are good additions before treating the repository as a more complete public project:
Add per-IP rate limiting to reduce abuse risk and protect the server from excessive UDP traffic.
Log request counts, invalid packets, client addresses, dropped packets, and server errors. For public deployments, consider JSON logs.
Add unit tests for:
- NTP timestamp encoding
- NTP version parsing
- NTP mode parsing
- Response packet construction
- Invalid packet handling
A cleaner structure could look like this:
src/
server.js
ntp.js
test/
ntp.test.jsThis makes the NTP packet logic easier to test.
Before sending responses, check whether the system clock is synchronized. On Linux, this could be done using tools such as timedatectl, chronyc, or another trusted source.
Add a small HTTP health endpoint on a separate port, for example:
GET /health
GET /metricsThis is useful for Docker, Kubernetes, uptime checks, and monitoring.
Add examples for:
- Ubuntu systemd service
- Docker deployment
- Docker Compose deployment
- Windows PowerShell usage
- Running behind firewall rules
The included workflow currently runs basic installation and syntax checks. Once tests are added, update the workflow to run them automatically.
Add ESLint and Prettier to keep code style consistent.
Document UDP firewall rules, rate limits, and why this server should not be considered production-ready without additional hardening.
.
├── .env.example
├── .github/
│ └── workflows/
│ └── ci.yml
├── .gitignore
├── Dockerfile
├── LICENSE
├── README.md
├── docker-compose.yml
├── ntp-server.js
└── package.jsonnpm startStarts the NTP server.
npm run checkRuns a basic JavaScript syntax check.
This project is licensed under the MIT License. See the LICENSE file for details.