Portainer Templates logo

Portainer Templates

Chrony NTP Chrony NTP

Container

OtherTools

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

Pulls: 12.9M
Architecture: amd64, arm64, arm/v7, arm/v6, 386, ppc64le, s390x
Image size: 7 MB
User: cturra
Created: Sep 17, 2017
Updated: 8 months ago
Status: active

Configuration

Type
Container
Platform
linux
Image
cturra/ntp:latest
Ports
123:123/udp
Env vars
LOG_LEVEL=0NTP_SERVERS=time.cloudflare.com
Restart
unless-stopped

Template by novaspirit

Notes

Template created by Pi-Hosted Series
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

Installation method

Install on Portainer

Import all app templates into your Portainer instance, for easy 1-click deploys

  1. Ensure both Docker and Portainer are installed, and up-to-date
  2. Log into your Portainer web UI
  3. Under Settings → App Templates, paste the below URL
  4. Head to Home → App Templates, and the list of apps will show up
  5. 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 demo

More install options in our documentation.

About this container

Docker Pulls Docker Stars GitHub Stars Apache licensed
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.
Linux x86-64 ARMv8 64-bit IBM POWER8 IBM Z Systems Linux x86/i686 ARMv7 32-bit ARMv6 32-bit

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/ntp

With 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 the 3.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 ntp

With 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_ntp

From 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.sh

Configure 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 an NTP_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 the NOCLIENTLOG=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 the LOG_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 a TZ 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 the NTP_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 has ntpdate 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 sec

If 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 found

To 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     : Normal

Here 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] +/-   11ms

Finally, 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   460us

Are 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 clock

Good 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.
Buy Me A Coffee

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: 137 means killed, usually out of memory. 126 or 127 means 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:123 only 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 login to 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 no so 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.

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:123 over 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 to 0
  • NTP_SERVERS, defaults to time.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.