diff --git a/README.md b/README.md
index 8fc93e144a12f601379ff56fbde08c9ce7cd8fb0..e6871b37d83156eb48ff4efc1c1210af85ba5745 100644
--- a/README.md
+++ b/README.md
@@ -111,6 +111,7 @@ vagrant up
 - [Adding/replacing a node](docs/nodes.md)
 - [Upgrades basics](docs/upgrades.md)
 - [Air-Gap installation](docs/offline-environment.md)
+- [NTP](docs/ntp.md)
 - [Hardening](docs/hardening.md)
 - [Roadmap](docs/roadmap.md)
 
diff --git a/docs/_sidebar.md b/docs/_sidebar.md
index 272d4a78623f03f001cb7ad824ae8a47696647e9..e4ac39795d80e3606d201baecc82537ae3102fe4 100644
--- a/docs/_sidebar.md
+++ b/docs/_sidebar.md
@@ -50,6 +50,7 @@
   * [DNS Stack](docs/dns-stack.md)
   * [Kubernetes reliability](docs/kubernetes-reliability.md)
   * [Local Registry](docs/kubernetes-apps/registry.md)
+  * [NTP](docs/ntp.md)
 * External Storage Provisioners
   * [RBD Provisioner](docs/kubernetes-apps/rbd_provisioner.md)
   * [CEPHFS Provisioner](docs/kubernetes-apps/cephfs_provisioner.md)
diff --git a/docs/ntp.md b/docs/ntp.md
new file mode 100644
index 0000000000000000000000000000000000000000..efa40b9341834ba70eff7f0fb3bff4f07c568be7
--- /dev/null
+++ b/docs/ntp.md
@@ -0,0 +1,41 @@
+# NTP synchronization
+
+The Network Time Protocol (NTP) is a networking protocol for clock synchronization between computer systems. Time synchronization is important to Kubernetes and Etcd.
+
+## Enable the NTP
+
+To start the ntpd(or chrony) service and enable it at system boot. There are related specific variables:
+
+```ShellSession
+ntp_enabled: true
+```
+
+The NTP service would be enabled and sync time automatically.
+
+## Custimize the NTP configure file
+
+In the Air-Gap environment, the node cannot access the NTP server by internet. So the node can use the customized ntp server by configuring ntp file.
+
+```ShellSession
+ntp_enabled: true
+ntp_manage_config: true
+ntp_servers:
+  - "0.your-ntp-server.org iburst"
+  - "1.your-ntp-server.org iburst"
+  - "2.your-ntp-server.org iburst"
+  - "3.your-ntp-server.org iburst"
+```
+
+## Advanced Configure
+
+Enable `tinker panic` is useful when running NTP in a VM environment to avoiding clock drift on VMs. It only takes effect when ntp_manage_config is true.
+
+```ShellSession
+ntp_tinker_panic: true
+```
+
+Force sync time immediately by NTP after the ntp installed, which is useful in newly installed system.
+
+```ShellSession
+ntp_force_sync_immediately: true
+```
diff --git a/inventory/sample/group_vars/all/all.yml b/inventory/sample/group_vars/all/all.yml
index ea69a5b2c1d7c3257f1dd43f2cf0b6a9efd3326b..c43d3a54c7a1f2b9ecacc94ab5379a07ae100f51 100644
--- a/inventory/sample/group_vars/all/all.yml
+++ b/inventory/sample/group_vars/all/all.yml
@@ -120,3 +120,13 @@ kube_webhook_token_auth_url_skip_tls_verify: false
 # kube_webhook_token_auth_url: https://...
 ## base64-encoded string of the webhook's CA certificate
 # kube_webhook_token_auth_ca_data: "LS0t..."
+
+## NTP Settings
+# Start the ntpd or chrony service and enable it at system boot.
+ntp_enabled: false
+ntp_manage_config: false
+ntp_servers:
+  - "0.pool.ntp.org iburst"
+  - "1.pool.ntp.org iburst"
+  - "2.pool.ntp.org iburst"
+  - "3.pool.ntp.org iburst"
diff --git a/roles/kubernetes/preinstall/defaults/main.yml b/roles/kubernetes/preinstall/defaults/main.yml
index 9624ea6fa1f921478aa5839216c734d38b37c0cb..e9091a64912337ab897e964958a23add51627ba4 100644
--- a/roles/kubernetes/preinstall/defaults/main.yml
+++ b/roles/kubernetes/preinstall/defaults/main.yml
@@ -62,3 +62,40 @@ pkg_install_retries: 4
 
 # Check if access_ip responds to ping. Set false if your firewall blocks ICMP.
 ping_access_ip: true
+
+## NTP Settings
+# Start the ntpd or chrony service and enable it at system boot.
+ntp_enabled: false
+# The package to install which provides NTP functionality.
+# The default is ntp for most platforms, or chrony on RHEL/CentOS 7 and later.
+# The ntp_package can be one of ['ntp','chrony']
+ntp_package: >-
+      {% if ansible_os_family == "RedHat" -%}
+      chrony
+      {%- else -%}
+      ntp
+      {%- endif -%}
+
+# Manage the NTP configuration file.
+ntp_manage_config: false
+# Specify the NTP servers
+# Only takes effect when ntp_manage_config is true.
+ntp_servers:
+  - "0.pool.ntp.org iburst"
+  - "1.pool.ntp.org iburst"
+  - "2.pool.ntp.org iburst"
+  - "3.pool.ntp.org iburst"
+# Restrict NTP access to these hosts.
+# Only takes effect when ntp_manage_config is true.
+ntp_restrict:
+  - "127.0.0.1"
+  - "::1"
+# The NTP driftfile path
+# Only takes effect when ntp_manage_config is true.
+ntp_driftfile: /var/lib/ntp/ntp.drift
+# Enable tinker panic is useful when running NTP in a VM environment.
+# Only takes effect when ntp_manage_config is true.
+ntp_tinker_panic: false
+
+# Force sync time immediately after the ntp installed, which is useful in in newly installed system.
+ntp_force_sync_immediately: false
diff --git a/roles/kubernetes/preinstall/handlers/main.yml b/roles/kubernetes/preinstall/handlers/main.yml
index 07883318680875200695e44e85c394e1ec4785bf..70c6414c97f6162ecec82896f912a42fd51fe92c 100644
--- a/roles/kubernetes/preinstall/handlers/main.yml
+++ b/roles/kubernetes/preinstall/handlers/main.yml
@@ -120,3 +120,9 @@
   service:
     name: systemd-resolved
     state: restarted
+
+- name: Preinstall | restart ntp
+  service:
+    name: "{{ ntp_service_name }}"
+    state: restarted
+  when: ntp_enabled
diff --git a/roles/kubernetes/preinstall/tasks/0081-ntp-configurations.yml b/roles/kubernetes/preinstall/tasks/0081-ntp-configurations.yml
new file mode 100644
index 0000000000000000000000000000000000000000..ba4578744be353431dc71a3262df96ff67e899d2
--- /dev/null
+++ b/roles/kubernetes/preinstall/tasks/0081-ntp-configurations.yml
@@ -0,0 +1,65 @@
+---
+- name: Ensure NTP package
+  package:
+    name:
+      - "{{ ntp_package }}"
+    state: present
+
+- name: Disable systemd-timesyncd
+  service:
+    name: systemd-timesyncd.service
+    enabled: false
+    state: stopped
+  failed_when: false
+
+- name: Set fact NTP settings
+  set_fact:
+    ntp_config_file: >-
+      {% if ntp_package == "ntp" -%}
+      /etc/ntp.conf
+      {%- elif ansible_os_family in ['RedHat', 'Suse'] -%}
+      /etc/chrony.conf
+      {%- else -%}
+      /etc/chrony/chrony.conf
+      {%- endif -%}
+    ntp_service_name: >-
+      {% if ntp_package == "chrony" -%}
+      chronyd
+      {%- elif ansible_os_family == 'RedHat' -%}
+      ntpd
+      {%- else -%}
+      ntp
+      {%- endif %}
+
+- name: Generate NTP configuration file.
+  template:
+    src: "{{ ntp_config_file | basename }}.j2"
+    dest: "{{ ntp_config_file }}"
+    mode: 0644
+  notify: Preinstall | restart ntp
+  when:
+    - ntp_manage_config
+
+- name: Stop the NTP Deamon For Sync Immediately   # `ntpd -gq`,`chronyd -q` requires the ntp daemon stop
+  service:
+    name: "{{ ntp_service_name }}"
+    state: stopped
+  when:
+    - ntp_force_sync_immediately
+
+- name: Force Sync NTP Immediately
+  command: >-
+      timeout -k 60s 60s
+      {% if ntp_package == "ntp" -%}
+      ntpd -gq
+      {%- else -%}
+      chronyd -q
+      {%- endif -%}
+  when:
+    - ntp_force_sync_immediately
+
+- name: Ensure NTP service is started and enabled
+  service:
+    name: "{{ ntp_service_name }}"
+    state: started
+    enabled: true
diff --git a/roles/kubernetes/preinstall/tasks/main.yml b/roles/kubernetes/preinstall/tasks/main.yml
index 495be4abb440ab7fe7e5f9acc33bb9e80b51947b..45fa3d14823bfe2a1d70517dea53cd98cb4a7db9 100644
--- a/roles/kubernetes/preinstall/tasks/main.yml
+++ b/roles/kubernetes/preinstall/tasks/main.yml
@@ -66,6 +66,13 @@
   tags:
     - bootstrap-os
 
+- import_tasks: 0081-ntp-configurations.yml
+  when:
+    - not dns_late
+    - ntp_enabled
+  tags:
+    - bootstrap-os
+
 - import_tasks: 0090-etchosts.yml
   when:
     - not dns_late
diff --git a/roles/kubernetes/preinstall/templates/chrony.conf.j2 b/roles/kubernetes/preinstall/templates/chrony.conf.j2
new file mode 100644
index 0000000000000000000000000000000000000000..226f9bc9920b3deda0bfb59914856d804b21b32a
--- /dev/null
+++ b/roles/kubernetes/preinstall/templates/chrony.conf.j2
@@ -0,0 +1,27 @@
+# {{ ansible_managed }}
+
+# Specify one or more NTP servers.
+# Use public servers from the pool.ntp.org project.
+# Please consider joining the pool (http://www.pool.ntp.org/join.html).
+{% for server in ntp_servers %}
+server {{ server }}
+{% endfor %}
+
+# Record the rate at which the system clock gains/losses time.
+driftfile /var/lib/chrony/drift
+
+{% if ntp_tinker_panic is sameas true %}
+# Force time sync if the drift exceeds the threshold specified
+# Usefull for VMs that can be paused and much later resumed.
+makestep 1.0 -1
+{% else %}
+# Allow the system clock to be stepped in the first three updates
+# if its offset is larger than 1 second.
+makestep 1.0 3
+{% endif %}
+
+# Enable kernel synchronization of the real-time clock (RTC).
+rtcsync
+
+# Specify directory for log files.
+logdir /var/log/chrony
diff --git a/roles/kubernetes/preinstall/templates/ntp.conf.j2 b/roles/kubernetes/preinstall/templates/ntp.conf.j2
new file mode 100644
index 0000000000000000000000000000000000000000..b49c2e4b2599c48fef071559e314ee3de7455da1
--- /dev/null
+++ b/roles/kubernetes/preinstall/templates/ntp.conf.j2
@@ -0,0 +1,45 @@
+# {{ ansible_managed }}
+
+# /etc/ntp.conf, configuration for ntpd; see ntp.conf(5) for help
+
+driftfile {{ ntp_driftfile }}
+
+{% if ntp_tinker_panic is sameas true %}
+# Always reset the clock, even if the new time is more than 1000s away
+# from the current system time. Usefull for VMs that can be paused
+# and much later resumed.
+tinker panic 0
+{% endif %}
+
+# Specify one or more NTP servers.
+# Use public servers from the pool.ntp.org project.
+# Please consider joining the pool (http://www.pool.ntp.org/join.html).
+{% for item in ntp_servers %}
+pool {{ item }}
+{% endfor %}
+
+# Access control configuration; see /usr/share/doc/ntp-doc/html/accopt.html for
+# details.  The web page <http://support.ntp.org/bin/view/Support/AccessRestrictions>
+# might also be helpful.
+#
+# Note that "restrict" applies to both servers and clients, so a configuration
+# that might be intended to block requests from certain clients could also end
+# up blocking replies from your own upstream servers.
+
+# By default, exchange time with everybody, but don't allow configuration.
+restrict -4 default kod notrap nomodify nopeer noquery limited
+restrict -6 default kod notrap nomodify nopeer noquery limited
+
+# Local users may interrogate the ntp server more closely.
+{% for item in ntp_restrict %}
+restrict {{ item }}
+{% endfor %}
+
+# Needed for adding pool entries
+restrict source notrap nomodify noquery
+
+# Disable the monitoring facility to prevent amplification attacks using ntpdc
+# monlist command when default restrict does not include the noquery flag. See
+# CVE-2013-5211 for more details.
+# Note: Monitoring will not be disabled with the limited restriction flag.
+disable monitor
diff --git a/tests/files/packet_almalinux8-calico.yml b/tests/files/packet_almalinux8-calico.yml
index 809df9e793a23cb820fb6012c028cdf42dbba166..f9ab1299ded0a21e37e94db3116375668a2c622b 100644
--- a/tests/files/packet_almalinux8-calico.yml
+++ b/tests/files/packet_almalinux8-calico.yml
@@ -9,3 +9,9 @@ metrics_server_enabled: true
 dashboard_namespace: "kube-dashboard"
 dashboard_enabled: true
 loadbalancer_apiserver_type: haproxy
+
+# NTP mangement
+ntp_enabled: true
+ntp_manage_config: true
+ntp_tinker_panic: true
+ntp_force_sync_immediately: true