---
tags: [linux, tooling]
verified: 2026-09-01
---

# Linux — время и синхронизация: chrony, timesyncd, RTC

## Обзор

Время — та зависимость, которую замечают только когда она ломается, и ломается она не там, где её ищут. Сертификат отвергается с формулировкой «ещё не действителен», хотя выдан вчера. Код из приложения-аутентификатора не подходит, хотя вводится верно. Логи с двух машин при разборе инцидента не сходятся, и восстановить порядок событий невозможно. Во всех трёх случаях виноваты часы, но ни одно сообщение об ошибке этого не говорит.

Причина в том, что время в системе — не одна величина, а несколько разных, живущих по разным правилам: аппаратные часы на плате, системные часы в ядре, монотонный счётчик для измерений и слой часовых поясов поверх всего этого. Большинство проблем возникает на стыках: значение берут из одного механизма, а ожидают свойств другого.

Статья разбирает эти механизмы, два демона синхронизации и, отдельно, диагностику — потому что чтение вывода `chronyc` требует понимания, что означают его поля, а без этого он выглядит набором чисел.

Версии-якоря: chrony 4.9 (август 2026), systemd 261.

## Что такое «время» в системе

### RTC и системные часы

Аппаратные часы (RTC, real-time clock) — микросхема на плате с собственным питанием от батарейки. Их единственная задача — помнить время, пока машина выключена. Ядро читает их один раз при загрузке, устанавливает по ним системные часы и дальше к ним практически не обращается: точность RTC невелика, разрешение обычно в секундах.

Системные часы ведёт ядро, отсчитывая от прочитанного при старте значения с помощью счётчика процессора. Именно они возвращаются программам, и именно их правит демон синхронизации.

В виртуальных машинах и на облачных VPS картина другая: RTC там эмулируется гипервизором, а точность после миграции или паузы гостя может быть какой угодно. На таких машинах демон синхронизации не «желателен для точности», а обязателен для работоспособности.

Отдельный случай — машины вовсе без батарейки RTC (одноплатники, часть встраиваемых устройств). Для них `systemd-timesyncd` держит файл `/var/lib/systemd/timesync/clock` и при каждой успешной синхронизации обновляет его время модификации; при следующей загрузке системные часы подтягиваются как минимум к этой отметке. Точным временем это не является, но обеспечивает главное — время не идёт назад между перезагрузками, а с ним не ломаются проверки сроков действия и сборка по временным меткам.

### UTC или локальное время в RTC

В самих часах не записано, в какой шкале они идут — это соглашение, которое операционная система должна знать заранее. В Linux оно хранится третьей строкой файла `/etc/adjtime`: `UTC` или `LOCAL`.

Правильный ответ — `UTC`, и он же по умолчанию. Локальное время в RTC создаёт проблему, точно описанную в документации `hwclock`: если система работает в момент перехода на летнее время, записанное в RTC значение будет скорректировано; если система в этот момент была выключена — прочитанное из RTC значение скорректировано не будет. Одна и та же машина получает разное время в зависимости от того, работала ли она в ночь перехода.

Классический источник боли — дуалбут с Windows, которая исторически держит RTC в локальном времени. Симптом узнаваем: после каждой смены системы часы уезжают ровно на величину смещения пояса. Правильное лечение — приучить Windows к UTC (параметр реестра `RealTimeIsUniversal`), а не переводить Linux на `LOCAL`: во втором случае вы получаете описанную выше несогласованность в обмен на видимость порядка.

Проверить и переключить:

```bash
timedatectl                              # строка «RTC in local TZ»
timedatectl set-local-rtc 0              # перевести RTC в UTC
```

### Какие часы читать: REALTIME против MONOTONIC

Ядро отдаёт несколько разных часов, и выбор между ними — не оптимизация, а вопрос корректности.

| Часы | Может ли прыгнуть | Учитывает NTP-подстройку | Идёт во время suspend |
|---|---|---|---|
| `CLOCK_REALTIME` | да, назад и вперёд | да | да |
| `CLOCK_MONOTONIC` | нет | да, плавно | нет |
| `CLOCK_MONOTONIC_RAW` | нет | нет | нет |
| `CLOCK_BOOTTIME` | нет | да, плавно | да |

`CLOCK_REALTIME` — настоящее календарное время, и его можно переставить: администратором, демоном синхронизации при шаге, гипервизором. Для «который час» это правильный источник и единственно возможный.

Для измерения длительности он не годится совсем. Классическая ошибка — засечь `CLOCK_REALTIME` до операции и после, вычесть. Если в промежуток попал шаг часов назад, разность получится отрицательной; если вперёд — операция «займёт» лишние секунды. Код, где таймаут вычисляется таким способом, при переводе часов либо срабатывает мгновенно, либо не срабатывает никогда, и воспроизвести это в тестах почти невозможно. Для длительностей нужен `CLOCK_MONOTONIC`: он не переставляется никогда.

Две тонкости, которые обычно упускают. Во-первых, `CLOCK_MONOTONIC` всё же не равномерен: он подвержен плавным подстройкам частоты от `adjtime()` и NTP — просто никогда не идёт назад. Если нужна именно аппаратная шкала без вмешательства демона синхронизации, есть `CLOCK_MONOTONIC_RAW`. Во-вторых, `CLOCK_MONOTONIC` не считает время, проведённое системой в suspend: на ноутбуке, закрытом на ночь, он «простоит» эту ночь. Если требуется монотонность с учётом сна — `CLOCK_BOOTTIME`.

## Демоны синхронизации

В Linux сегодня два реальных кандидата, и разница между ними принципиальная, а не в удобстве.

`systemd-timesyncd` реализует только SNTP — упрощённый клиентский протокол. Он опрашивает сервер, получает время, правит часы. Ни серверной части, ни сложной дисциплины часов, ни работы с несколькими источниками одновременно у него нет, и документация systemd это оговаривает прямо: сценарии, требующие полноценного NTP, им не покрываются.

chrony — полная реализация NTP: несколько источников с оценкой качества каждого, отсев источников с неверным временем, статистическая фильтрация, собственная модель дрейфа часов, режим сервера для других машин, аппаратные штампы времени и NTS.

| | systemd-timesyncd | chrony 4.9 |
|---|---|---|
| Протокол | SNTP (клиент) | NTP полностью |
| Несколько источников одновременно | нет | да, с комбинированием |
| Отсев источника с неверным временем | нет | да (`falseticker`) |
| Модель дрейфа часов | нет | да, компенсирует уход между опросами |
| Работа сервером для парка машин | нет | да |
| Плохая сеть, редкая связь, ноутбук | посредственно | целевой сценарий |
| Диагностика | `timedatectl show-timesync` | `chronyc tracking`, `sources`, `sourcestats` |

**Когда чего хватает.** Для десктопа или ноутбука, которому нужно «примерно верное время», `timesyncd` достаточен, и он уже установлен. Для сервера — chrony, и причина не в точности как таковой, а в отсеве плохих источников: SNTP-клиент верит единственному серверу, к которому обратился, и если тот отдаёт неверное время, поедет вслед за ним без единого возражения. Для машины, выдающей TOTP-коды, подписывающей что-либо или участвующей в Kerberos, это неприемлемый режим.

Одновременно запускать оба нельзя — они будут драться за системные часы. Установка chrony в большинстве дистрибутивов маскирует `systemd-timesyncd` автоматически, но проверить стоит: `systemctl status systemd-timesyncd` должен показывать `masked` или `inactive`.

### Дисциплина часов: подтягивать или переставлять

Исправить отставшие часы можно двумя способами, и они не равноценны.

**Slew** (подтягивание) — изменить скорость хода: часы идут чуть быстрее, пока не догонят. Время при этом остаётся монотонным, ни одна отметка не повторяется и не пропадает. Плата — медленно: скорость подтягивания ограничена, и на Linux `chronyd` может править частоту в пределах до 100000 ppm (10%), то есть даже теоретический максимум даёт лишь несколько секунд в минуту, а реальный предел задаётся директивой `maxslewrate` и обычно много ниже.

**Step** (шаг) — переставить часы одномоментно. Быстро, но время скачет: при шаге назад отметки повторяются, при шаге вперёд возникает разрыв. Всё, что опирается на монотонность `CLOCK_REALTIME`, в этот момент может сломаться — сработать разом или зависнуть.

Отсюда правило: подтягивать всегда, переставлять только на старте, пока прикладной софт ещё не запущен. Именно это и делает директива chrony:

```conf
makestep 1.0 3
```

Первый аргумент — порог в секундах, второй — предел по числу обновлений. Читается так: переставить часы шагом, если расхождение больше секунды, но только в первых трёх обновлениях после запуска `chronyd`. Дальше — только плавное подтягивание. Документация chrony формулирует смысл прямо: на большинстве систем часы желательно переставлять только при загрузке, до старта программ, которые полагаются на монотонное движение времени вперёд.

Отрицательное значение второго аргумента снимает ограничение — то есть разрешает шаг в любой момент. Это то, что иногда советуют в интернете как лекарство от «часы всё равно уезжают», и это лечение симптома ценой создания нового класса багов. Если часы уезжают настолько, что подтягивание не справляется, проблема в источнике времени или в железе, а не в осторожности демона.

### Выбор источников

Источник времени задаётся директивами `server` (конкретный хост) и `pool` (имя, за которым скрывается набор адресов):

```conf
pool 2.pool.ntp.org iburst
```

`iburst` ускоряет первую синхронизацию, отправляя серию запросов вместо одного, — на старте это разница между секундами и минутами.

Stratum — расстояние от эталона: stratum 1 берёт время напрямую от источника (GPS, атомные часы), stratum 2 синхронизирован по stratum 1 и так далее. Обращаться к stratum 1 без надобности не нужно: их немного, они перегружены, а разница для обычной машины неразличима.

Для парка машин правильная схема — один-два собственных NTP-сервера, синхронизированных наружу, и все остальные машины по ним. Это даёт две вещи: во-первых, машины парка согласованы между собой, что важнее абсолютной точности, когда вы сравниваете их логи; во-вторых, наружу ходят двое, а не сотня.

## Диагностика

### Что показывает chronyc

```bash
chronyc tracking
```

Из вывода практически значимы четыре величины.

`Last offset` — расхождение на последнем обновлении. Мгновенное значение, скачет; само по себе мало о чём говорит.

`RMS offset` — усреднённое расхождение за длительный срок. Вот это и есть ответ на вопрос «насколько точны часы»: значения в единицы миллисекунд для машины в интернете нормальны, устойчивые сотни миллисекунд означают проблему.

`Frequency` — на сколько ppm часы уходили бы без коррекции. Это свойство железа, а не сети. Величина в единицы-десятки ppm обычна; сотни означают либо плохой генератор, либо, чаще, виртуализацию.

`Root dispersion` вместе с `Root delay` дают верхнюю оценку погрешности: сумма накопленной по цепочке неопределённости и половины сетевой задержки. Если требуется утверждение «наше время верно с точностью до X» — оно строится отсюда, а не из `Last offset`.

```bash
chronyc sources
```

Первые два символа строки — самое информативное в выводе. Первый обозначает тип источника (`^` — сервер, `=` — peer, `#` — локальные часы), второй — состояние выбора:

| Символ | Значение |
|---|---|
| `*` | выбран основным источником |
| `+` | участвует в комбинировании с основным |
| `-` | пригоден, но не выбран |
| `x` | falseticker: время расходится с большинством остальных |
| `~` | слишком большой разброс измерений |
| `?` | непригоден: недоступен, не синхронизирован или мало измерений |

`x` напротив источника — не мелочь, а сообщение о том, что кто-то отдаёт неправильное время; именно эту ситуацию SNTP-клиент распознать не способен в принципе.

Колонка `Reach` читается как восьмеричное число: каждый бит — успех или неуспех одного из последних восьми опросов. `377` — все восемь удались, `0` — источник недоступен, промежуточные значения означают потери. Это самый быстрый способ отличить «сервер отвечает через раз» от «сервер не отвечает вообще».

### timedatectl

```bash
timedatectl                        # общая картина: пояс, RTC, статус синхронизации
timedatectl show-timesync --all    # детали, если работает timesyncd
timedatectl set-ntp true           # включить синхронизацию
```

Строка `System clock synchronized: yes` означает лишь, что демон однажды успешно синхронизировался, а не что часы верны прямо сейчас. Для реального ответа нужен `chronyc tracking`.

### Симптом → причина

| Что видно | Где искать |
|---|---|
| TLS: сертификат «ещё не действителен» или «истёк», хотя срок в порядке | часы клиента ушли; сверить `timedatectl` с внешним источником |
| TOTP-коды не принимаются, хотя вводятся верно | расхождение больше окна (обычно ±30 с) на любой из двух сторон |
| Kerberos: «clock skew too great» | расхождение больше допуска (по умолчанию 5 минут) |
| Логи двух машин не сходятся по порядку событий | машины синхронизированы с разными источниками или одна не синхронизирована вовсе |
| Таймер systemd отработал дважды или пропустил запуск | переход на летнее время в локальном поясе |
| Часы уезжают, несмотря на работающий демон | `chronyc sources`: все источники `?` или `x`, либо блокируется UDP/123 |
| Огромный `Frequency`, часы плывут после каждой паузы | виртуализация без корректной синхронизации с гипервизором |

## Часовые пояса

Пояс — это слой отображения поверх системных часов: ядро оперирует UTC, а преобразование в локальное время делает библиотека по данным из tzdata. `/etc/localtime` — символьная ссылка на файл зоны в `/usr/share/zoneinfo`.

```bash
timedatectl list-timezones | grep Moscow
timedatectl set-timezone Europe/Moscow
```

Практическое следствие: tzdata — это данные, которые устаревают. Правила перехода на летнее время и смещения поясов меняются решениями правительств, иногда за недели до вступления в силу. Машина с неактуальным пакетом tzdata показывает верное UTC и неверное локальное время, причём молча. Для сервера это ещё один довод держать всё в UTC: серверу локальное время нужно ровно в одном месте — при показе человеку.

Переход на летнее время — регулярный источник багов в расписаниях. Осенью час повторяется, весной час не существует; задание, назначенное на этот час, соответственно либо выполняется дважды, либо не выполняется. Для systemd-таймеров лечение простое: `OnCalendar=` понимает суффикс `UTC`.

```ini
OnCalendar=*-*-* 02:30:00 UTC
```

Для заданий, которые обязаны идти именно раз в сутки, это правильный выбор; привязка к локальному времени оправдана только там, где смысл задания связан с человеческим распорядком.

Отдельная категория — високосная секунда: вставка лишней секунды в UTC для согласования с вращением Земли. Для системы это разрыв монотонности `CLOCK_REALTIME` со всеми вытекающими, и крупные инфраструктуры давно обходят его «размазыванием» секунды по суткам (leap smearing). Резолюцией 4 Генеральной конференции по мерам и весам 2022 года принято решение прекратить добавление високосных секунд — не позднее 2035 года; окончательные детали ещё дорабатываются, и до тех пор секунды добавляются по прежним правилам. Практический вывод на сегодня прежний: если инфраструктура зависит от непрерывности времени, источники NTP должны быть согласованы в том, как они этот момент обрабатывают — смешивать «размазывающие» и обычные серверы нельзя.

## Сравнение с FreeBSD

В базовой системе FreeBSD лежит `ntpd` — референсная реализация от ntp.org, та самая, на смену которой chrony пришёл в большинстве Linux-дистрибутивов. Включается он в `rc.conf`:

```sh
ntpd_enable="YES"
ntpd_sync_on_start="YES"
```

Второй переключатель — прямой аналог решения задачи «на старте часы могут быть далеко»: он запускает `ntpd` с флагом `-g`, разрешающим однократную большую коррекцию при запуске. Документация `rc.conf(5)` называет его предпочтительной заменой утилите `ntpdate`, которая выводится из состава дистрибутива NTP. Если вы встречаете совет «добавьте `ntpdate` в загрузку» — он устарел.

Различия по существу:

- Идеология та же, что и везде во FreeBSD: в базе лежит рабочий и консервативный вариант, а более современный инструмент берётся из портов. chrony есть в портах и работает так же, как в Linux, включая конфигурацию.
- Предел скорости подтягивания у ядра ниже: 5000 ppm против 100000 ppm в Linux, причём, как отмечает документация chrony, установка `maxslewrate` во FreeBSD в диапазоне от 500 до 5000 ppm фактически даёт 500 ppm. Практического значения для обычной эксплуатации это не имеет, но объясняет, почему сильно ушедшие часы там подтягиваются дольше.
- Управление RTC — через `sysctl machdep.wall_cmos_clock` и `machdep.adjkerntz` вместо `/etc/adjtime`; смысл настройки тот же, и правильный ответ тот же — держать RTC в UTC.
- Слой часовых поясов идентичен: тот же tzdata, тот же `/etc/localtime`, настраивается через `tzsetup`.

## Источники

- [chrony documentation](https://chrony-project.org/documentation.html) — `makestep`, `maxslewrate`, поля `tracking` и легенда `sources`
- [chrony NEWS](https://github.com/mlichvar/chrony/blob/master/NEWS) — состав релизов, версия 4.9
- [systemd-timesyncd.service(8)](https://www.freedesktop.org/software/systemd/man/systemd-timesyncd.service.html) — «implements SNTP only», файл `clock` для машин без RTC
- [clock_gettime(2)](https://man7.org/linux/man-pages/man2/clock_gettime.2.html) — поведение `CLOCK_REALTIME`, `CLOCK_MONOTONIC`, `CLOCK_MONOTONIC_RAW`, `CLOCK_BOOTTIME`
- [hwclock(8)](https://man7.org/linux/man-pages/man8/hwclock.8.html) — `/etc/adjtime`, несогласованность локального времени в RTC при переходе на летнее
- [rc.conf(5), FreeBSD](https://man.freebsd.org/cgi/man.cgi?rc.conf5) — `ntpd_sync_on_start` и статус `ntpdate`
- [Резолюции CGPM 2022](https://www.bipm.org/en/committees/cg/cgpm/27-2022) — решение о прекращении добавления високосных секунд

---

## Связанные заметки

- [Linux — systemd: init, сервисы, таймеры](./linux-systemd.md) — таймеры, которые ломает переход на летнее время, и `OnCalendar=` в UTC
- [Linux — мониторинг и отладка](./linux-monitoring-debugging.md) — сведение логов нескольких машин, ради которого нужны согласованные часы
- [Linux — безопасность: hardening, SELinux/AppArmor](./linux-security-hardening.md) — TOTP, сроки действия сертификатов и метки времени в аудите
- [Обратный прокси и TLS](./reverse-proxy-tls.md) — сроки действия сертификатов с другой стороны: почему расхождение часов выглядит как отзыв сертификата
- [Linux — пользователи, группы, PAM и systemd-logind](./linux-users-pam.md) — где в стеке аутентификации оказывается TOTP-модуль, чувствительный к часам
