Chrony NTP
Container
chrony is a versatile implementation of the Network Time Protocol (NTP). It can synchronise the system clock with NTP servers, reference clocks (e.g. GPS receiver), and manual input using wristwatch and keyboard. It can also operate as an NTPv4 (RFC 5905) server and peer to provide a time service to other computers in the network.
Image details
Configuration
TypeContainerlinuxcturra/ntp:latest123:123/udpLOG_LEVEL=0NTP_SERVERS=time.cloudflare.comunless-stoppedTemplate by novaspirit
Notes
Check our Github page: https://github.com/pi-hosted/pi-hosted
Official Webpage: https://github.com/cturra/docker-ntp
Official Docker Documentation: https://github.com/cturra/docker-ntp/blob/main/README.md
Standalone Install
Select an install method, to see config/commands for deploying Chrony NTP
Install on Portainer
Import all app templates into your Portainer instance, for easy 1-click deploys
- Ensure both Docker and Portainer are installed, and up-to-date
- Log into your Portainer web UI
- Under Settings → App Templates, paste the below URL
- Head to Home → App Templates, and the list of apps will show up
- Select Chrony NTP, fill in any config options, and hit Deploy
Template Import URL
https://raw.githubusercontent.com/Lissy93/portainer-templates/main/templates.json
Show Me
More install options in our documentation.
About this container
This container runs chrony on Alpine Linux.
chrony is a versatile implementation of the Network Time Protocol (NTP). It can synchronise the system clock with NTP servers, reference clocks (e.g. GPS receiver), and manual input using wristwatch and keyboard. It can also operate as an NTPv4 (RFC 5905) server and peer to provide a time service to other computers in the network.
Supported Architectures
Architectures officially supported by this Docker container. Simply pulling this container from Docker Hub should retrieve the correct image for your architecture.How to Run this container
With the Docker CLI
Pull and run -- it's this simple.# pull from docker hub
$> docker pull cturra/ntp
# run ntp
$> docker run --name=ntp \
--restart=always \
--detach \
--publish=123:123/udp \
cturra/ntp
# OR run ntp with higher security
$> docker run --name=ntp \
--restart=always \
--detach \
--publish=123:123/udp \
--read-only \
--tmpfs=/etc/chrony:rw,mode=1750 \
--tmpfs=/run/chrony:rw,mode=1750 \
--tmpfs=/var/lib/chrony:rw,mode=1750 \
cturra/ntpWith Docker Compose
Using the docker-compose.yml file included in this git repo, you can build the container yourself (should you choose to). Note: this docker-compose files uses the3.9 compose format, which requires Docker Engine release 19.03.0+# run ntp
$> docker compose up -d ntp
# (optional) check the ntp logs
$> docker compose logs ntpWith Docker Swarm
(These instructions assume you already have a swarm)# deploy ntp stack to the swarm
$> docker stack deploy -c docker-compose.yml cturra
# check that service is running
$> docker stack services cturra
# (optional) view the ntp logs
$> docker service logs -f cturra_ntpFrom a Local command line
Using the vars file in this git repo, you can update any of the variables to reflect your environment. Once updated, simply execute the build then run scripts.# build ntp
$> ./build.sh
# run ntp
$> ./run.shConfigure NTP Servers
By default, this container uses CloudFlare's time server (time.cloudflare.com). If you'd like to use one or more different NTP server(s), you can pass this container anNTP_SERVERS
environment variable. This can be done by updating the vars, docker-compose.yml
files or manually passing --env=NTP_SERVERS="..." to docker run.Below are some examples of how to configure common NTP Servers.
Do note, to configure more than one server, you must use a comma delimited list WITHOUT spaces.
# (default) cloudflare
NTP_SERVERS="time.cloudflare.com"
# google
NTP_SERVERS="time1.google.com,time2.google.com,time3.google.com,time4.google.com"
# alibaba
NTP_SERVERS="ntp1.aliyun.com,ntp2.aliyun.com,ntp3.aliyun.com,ntp4.aliyun.com"
# local (offline)
NTP_SERVERS="127.127.1.1"If you're interested in a public list of stratum 1 servers, you can have a look at the following list. Do make sure to verify the ntp server is active as this list does appaer to have some no longer active servers.
https://www.advtimesync.com/docs/manual/stratum1.html
Chronyd Options
No Client Log (noclientlog)
This is optional and not enabled by default. If you provide theNOCLIENTLOG=true envivonrment variable,
chrony will be configured to:Specifies that client accesses are not to be logged. Normally they are logged, allowing statistics to be reported using the clients command in chronyc. This option also effectively disables server support for the NTP interleaved mode.
Logging
By default, this project logs informational messages to stdout, which can be helpful when running the ntp service. If you'd like to change the level of log verbosity, pass theLOG_LEVEL environment
variable to the container, specifying the level (#) when you first start it. This option matches
the chrony -L option, which support the following levels can to specified: 0 (informational), 1
(warning), 2 (non-fatal error), and 3 (fatal error).Feel free to check out the project documentation for more information at:
https://chrony.tuxfamily.org/doc/4.1/chronyd.html
Setting your timezone
By default the UTC timezone is used, however if you'd like to adjust your NTP server to be running in your local timezone, all you need to do is provide aTZ environment variable following the standard TZ data format.
As an example, using docker-compose.yaml, that would look like this if you were located in Vancouver, Canada:...
environment:
- TZ=America/Vancouver
...Enable Network Time Security
If all theNTP_SERVERS you have configured support NTS (Network Time Security) you can pass the ENABLE_NTS=true
option to the container to enable it. As an example, using docker-compose.yaml, that would look like this:...
environment:
- NTP_SERVERS=time.cloudflare.com
- ENABLE_NTS=true
...If any of the
NTP_SERVERS you have configured does not support NTS, you will see a message like the
following during startup:NTS-KE session with 164.67.62.194:4460 (tick.ucla.edu) timed out
Testing your NTP Container
From any machine that hasntpdate you can query your new NTP container with the follow
command:$> ntpdate -q <DOCKER_HOST_IP>Here is a sample output from my environment:
$> ntpdate -q 10.13.13.9
server 10.13.1.109, stratum 4, offset 0.000642, delay 0.02805
14 Mar 19:21:29 ntpdate[26834]: adjust time server 10.13.13.109 offset 0.000642 secIf you see a message, like the following, it's likely the clock is not yet synchronized. You should see this go away if you wait a bit longer and query again.
$> ntpdate -q 10.13.13.9
server 10.13.13.9, stratum 16, offset 0.005689, delay 0.02837
11 Dec 09:47:53 ntpdate[26030]: no server suitable for synchronization foundTo see details on the ntp status of your container, you can check with the command below on your docker host:
$> docker exec ntp chronyc tracking
Reference ID : D8EF2300 (time1.google.com)
Stratum : 2
Ref time (UTC) : Sun Mar 15 04:33:30 2020
System time : 0.000054161 seconds slow of NTP time
Last offset : -0.000015060 seconds
RMS offset : 0.000206534 seconds
Frequency : 5.626 ppm fast
Residual freq : -0.001 ppm
Skew : 0.118 ppm
Root delay : 0.022015510 seconds
Root dispersion : 0.001476757 seconds
Update interval : 1025.2 seconds
Leap status : NormalHere is how you can see a peer list to verify the state of each ntp source configured:
$> docker exec ntp chronyc sources
210 Number of sources = 2
MS Name/IP address Stratum Poll Reach LastRx Last sample
===============================================================================
^+ time.cloudflare.com 3 10 377 404 -623us[ -623us] +/- 24ms
^* time1.google.com 1 10 377 1023 +259us[ +244us] +/- 11msFinally, if you'd like to see statistics about the collected measurements of each ntp source configured:
$> docker exec ntp chronyc sourcestats
210 Number of sources = 2
Name/IP Address NP NR Span Frequency Freq Skew Offset Std Dev
==============================================================================
time.cloudflare.com 35 18 139m +0.014 0.141 -662us 530us
time1.google.com 33 13 128m -0.007 0.138 +318us 460usAre you seeing messages like these and wondering what is going on?
$ docker logs -f ntps
[...]
2021-05-25T18:41:40Z System clock wrong by -2.535004 seconds
2021-05-25T18:41:40Z Could not step system clock
2021-05-25T18:42:47Z System clock wrong by -2.541034 seconds
2021-05-25T18:42:47Z Could not step system clockGood question! Since
chronyd is running with the -x flag, it will not try to control
the system (container host) clock. This of course is necessary because the process does not
have priviledge (for good reason) to modify the clock on the system.Like any host on your network, simply use your preferred ntp client to pull the time from the running ntp container on your container host.
Check the logs first
Nine times out of ten the logs tell you exactly what went wrong.
- In Portainer, go to Containers, click the container, then Logs. Or run
docker logs chrony-ntp - Exit codes help too:
137means killed, usually out of memory.126or127means the command inside the image is broken.
Port already in use
If deployment fails with "Bind for 0.0.0.0:123 failed: port is already allocated", something else on your server is using that port.
- Find what's using it:
sudo ss -tlnp | grep :123 - Stop the other service, or pick a different host port. In
123:123only the left number is yours to change, the right one belongs to the app.
Image won't pull
Test the pull directly on the host: docker pull cturra/ntp:latest
- "manifest unknown" means the tag no longer exists. This template uses
latest, so try pinning a specific version instead. - "toomanyrequests" is the Docker Hub rate limit. Log in with
docker loginto raise it. - "no space left on device" means a full disk. Reclaim space with
docker system prune
"exec format error"
This means the image was built for a different CPU architecture than your server.
- This image supports:
amd64, arm64, arm/v7, arm/v6, 386, ppc64le, s390x - Check yours with
uname -m: x86_64 is amd64, aarch64 is arm64. Raspberry Pi and other ARM boards are the usual culprits.
Container keeps restarting
The unless-stopped restart policy relaunches the app after every crash, so the real error can scroll past.
- Check the logs right after a restart, the last few lines before it died are the useful ones.
- Get the exit code with
docker inspect chrony-ntp --format '{{.State.ExitCode}}' - Still stuck? Redeploy once with the restart policy set to
noso the failure stays visible.
Data disappears when the container is recreated
This template doesn't define any volumes, so everything chrony-ntp saves lives inside the container and is lost on update or recreate.
- Add a volume mapping for chrony-ntp's data folder before storing anything you care about.
Raise an issue
Found something which isn't working as it should? Here's how to report it.
- Bug within the app: Open an issue within chrony-ntp's repo
- Template not working: Open an issue on novaspirit/pi-hosted
- This website not working: Open an issue on lissy93/portainer-templates
A single container
Chrony NTP runs as one container, the simplest kind of app here. Just the one image to pull and nothing else wired up alongside it.
The app image
An image is the app packed up ready to go, everything Chrony NTP needs bundled into one download. This template pulls cturra/ntp:latest, which Docker fetches once (about 7 MB) and then starts your own copy from.
Where the image comes from
Docker pulls its images from registries, public libraries of ready-built apps. Chrony NTP's comes from Docker Hub, published by cturra.
Version tags
The bit after the colon in the image name is the version tag. Here it's latest, which always points at the newest build, so a redeploy can bump you to a newer release without you asking. Pin a specific tag if you would rather stay on one version.
Which machines it runs on
Every image is built for particular CPU types. This one ships for amd64, arm64, arm/v7, arm/v6, 386, ppc64le, s390x, so it runs on both regular x86 servers and ARM boards like a Raspberry Pi.
Ports
A port is the door the app answers on. A mapping like 123:123 means it's reachable on port 123 of your server, where the left number is yours to change and the right one belongs to the app. It opens:
123:123over UDP
No stored data
This template doesn't mount any storage, so whatever Chrony NTP writes stays inside the container and is wiped if it's recreated or updated. That's fine for something stateless, but add a volume before trusting it with anything you want to keep.
Environment variables
Environment variables are the settings you hand over when you deploy, things like a password or a timezone. Chrony NTP takes 2 of them, all with defaults you can leave alone or tweak:
LOG_LEVEL, defaults to0NTP_SERVERS, defaults totime.cloudflare.com. Additional Example: time1.google.com,time2.google.com,time3.google.com,time4.google.com
Restart policy
The restart policy here is unless-stopped, so Docker restarts Chrony NTP after a crash or reboot, but leaves it off when you stop it on purpose. You can change this on the deploy screen. The choices are no (never restart), on-failure (only after a crash), unless-stopped (restart unless you stop it), and always (bring it back no matter what).
Networking
Nothing custom is set, so Chrony NTP sits on Docker's default bridge network: its own private space that reaches the outside world only through the ports it publishes.
Container name
Once it's deployed, Portainer names the container chrony-ntp. That's what you'll spot in the containers list and use in commands like docker logs chrony-ntp.
Platform
The platform is linux, the kind of system the container is built to run on. Docker and Portainer handle this on a normal Linux server.
Portainer app templates
Zooming out, this whole page comes from a Portainer app template: a short recipe telling Portainer how to set Chrony NTP up. Add the template list to Portainer once, then deploying Chrony NTP is a click rather than a wall of config.