Configurar la zona horaria y la sincronización horaria en un servidor Linux

Publicado el 18 min de lectura

Un reloj de servidor mal ajustado nunca se anuncia como reloj, sino como certificado rechazado, como fuente de paquetes que apt no acepta o como cron job a la hora equivocada. Cómo ajustar la zona horaria y el servicio de tiempo, y cómo demostrar que la sincronización funciona.

Nadie se fija en el reloj de un servidor mientras va bien. Cuando se desajusta, el aviso nunca llega del reloj: llega en forma de certificado rechazado, de fuente de paquetes que apt no acepta, de cron job a la hora equivocada y de un log que ya no se puede relacionar con ningún otro.

Este artículo profundiza en el paso 5 de la checklist para un servidor root nuevo. Si ya has ejecutado allí los dos comandos, has cumplido con lo obligatorio. Aquí va todo lo demás: la elección del servicio de tiempo, el reloj hardware, UTC frente a hora local, la prueba de que la sincronización funciona y los mensajes de error que aparecen por el camino.

Todos los datos se refieren a Debian 13 (trixie), Debian 12 (bookworm), Ubuntu 24.04 LTS y Ubuntu 22.04 LTS. Los comandos están escritos para ejecutarse como root. Si trabajas como usuario normal, antepón sudo a cada comando.

Lo que una hora mal ajustada rompe de verdad

El denominador común: ninguno de estos mensajes habla del reloj.

  • Certificados TLS. Cada certificado tiene una ventana de validez. Si la hora del sistema queda por debajo, curl avisa con curl: (60) SSL certificate problem: certificate is not yet valid; si queda por encima, con certificate has expired. Afecta a cualquier llamada saliente.
  • apt. Los archivos Release llevan una fecha de emisión y otra de caducidad. Si el reloj se atrasa, aparece Release file for ... is not valid yet; si se adelanta mucho, sale is expired. El servidor deja entonces de recibir actualizaciones de seguridad sin que se caiga ningún servicio.
  • Los logs. Dos máquinas con cinco minutos de desviación ya no se pueden leer una contra otra, y es justo entonces cuando hace falta: en una intrusión o en una caída.
  • Cron jobs y timers de systemd. cron calcula en la zona horaria del sistema. Si cambias la zona más tarde, desplazas todos los jobs. Y si el reloj da un salto, los jobs se ejecutan dos veces o ninguna. En detalle, en configurar un cron job.
  • Los backups. Casi todos los backups nombran sus carpetas por la fecha y borran por antigüedad. Un reloj que retrocede un día sobrescribe el backup de ayer.
  • Contraseñas de un solo uso basadas en tiempo. TOTP trabaja en ventanas de 30 segundos, con una tolerancia de una ventana a cada lado en la mayoría de las implementaciones. Si el reloj del servidor se desvía más que eso, se rechaza cualquier código correcto. Es el único punto de esta lista que de verdad te deja fuera.

Antes de cambiar nada: vía de regreso e inventario

La vía de regreso

Cambiar la zona horaria no corta ninguna sesión SSH abierta, y activar la sincronización tampoco tiene consecuencias. Dos cosas de este artículo sí las tienen: un reloj que da un salto grande y el cambio de servicio de tiempo. Un salto grande hacia atrás deja temporalmente inválidos certificados que son válidos y tira por tierra los inicios de sesión con TOTP. El cambio de servicio de tiempo elimina el antiguo antes de que funcione el nuevo: si la instalación se interrumpe justo ahí, el servidor se queda sin ninguna sincronización horaria, y eso no se nota hasta días después.

Los servidores root KVM y los servidores dedicados de KernelHost no tienen IPMI ni iDRAC. El acceso que funciona sin servicios de red dentro del sistema invitado es la consola VNC del área de cliente. Cuelga de la capa de virtualización o de la propia conexión, así que un reloj mal ajustado en el invitado no le afecta. Entra ahí una vez antes de empezar y comprueba que conoces la contraseña de root.

El estado actual en cuatro comandos

Anota lo que hay ahora mismo. Sin esa nota, más adelante no sabrás si una desviación es nueva:

timedatectl
readlink -f /etc/localtime
date -u
systemctl is-active systemd-timesyncd chrony

De las siete líneas que devuelve timedatectl cuentan tres. Time zone indica la zona con su abreviatura y su desfase, por ejemplo Europe/Vienna (CEST, +0200). System clock synchronized dice si el reloj se ha sincronizado alguna vez. NTP service tiene tres estados que se confunden con frecuencia:

LíneaSignificadoQué hacer
NTP service: activeHay un servicio de tiempo instalado y en marcha.Nada, pero conviene comprobarlo igualmente.
NTP service: inactiveHay un servicio de tiempo instalado, pero no está en marcha.Arranca el servicio y busca la causa en el journal.
NTP service: n/aNo hay ningún servicio de tiempo instalado.Instala timesyncd o chrony.

En el tercer caso, timedatectl show -p CanNTP --value devuelve no, y cualquier intento de activar la sincronización acaba en Failed to set ntp: NTP not supported.

La zona horaria y la sincronización horaria son dos cosas distintas

El kernel lleva exactamente un contador: segundos desde el 1 de enero de 1970, contados en UTC. Ese contador es la hora del sistema y no sabe nada de zonas horarias, porque la zona es solo una cuestión de representación. De ahí salen dos consecuencias. Cambiar la zona horaria no desplaza ni un instante, date -u devuelve la misma salida antes y después. Y sincronizar el reloj cambia el contador, no la representación, así que una zona mal puesta sigue estando mal. Dos tareas, dos comprobaciones.

Paso 1: ajustar la zona horaria

Los nombres de zona vienen de la base de datos IANA y casi siempre tienen la forma Continente/Ciudad, en grafía inglesa. Busca el nombre en lugar de adivinarlo:

timedatectl list-timezones | grep -i vienna
timedatectl set-timezone Europe/Vienna

Comprobación:

timedatectl show -p Timezone --value
readlink -f /etc/localtime
date +%Z

Lo esperado es Europe/Vienna, la ruta /usr/share/zoneinfo/Europe/Vienna y una abreviatura acorde con la época del año: CEST en verano y CET en invierno.

Tres trampas

El archivo /etc/timezone no es el que manda. Debian 13 ya no lo incluye, allí un cat /etc/timezone termina con No such file or directory, y tzdata tampoco lo devuelve. En los otros tres sistemas sigue existiendo, pero en todos ellos lo decisivo es únicamente el symlink /etc/localtime.

Sin tzdata no hay zonas. En imágenes reducidas, sobre todo en Ubuntu, falta la base de datos de zonas. Entonces incluso un nombre bien escrito falla con Failed to set time zone: Invalid or not installed time zone 'Europe/Vienna', y timedatectl list-timezones devuelve una sola línea. Después de instalarla tienen que salir varios cientos:

DEBIAN_FRONTEND=noninteractive apt-get install -y tzdata
timedatectl list-timezones | wc -l

Los servicios en marcha se quedan con la zona antigua. cron, las bases de datos y los servidores de aplicaciones leen la zona horaria al arrancar y, tras un cambio, siguen escribiendo sus logs en la zona antigua hasta que los reinicias.

En entornos sin systemd en marcha no existe timedatectl. La vía clásica lleva al mismo resultado:

ln -sf /usr/share/zoneinfo/Europe/Vienna /etc/localtime
dpkg-reconfigure -f noninteractive tzdata
readlink -f /etc/localtime

UTC u hora local: una decisión consciente

Muchos operadores dejan sus servidores en UTC, y hay buenas razones para ello. UTC no conoce el horario de verano, así que no hay ninguna hora que ocurra dos veces ni ninguna que falte: la noche del cambio, las 02:30 en hora local existen dos veces en otoño y ninguna en primavera, lo que afecta a cualquier job dentro de esa ventana. En contra juega que son personas las que leen los logs y que una ventana de mantenimiento "los domingos a las 03:00" se refiere a la hora local. La regla práctica: si gestionas varios servidores o varias ubicaciones, usa UTC; si llevas un único servidor según el reloj de la oficina, usa la hora local. Lo único incorrecto es no saberlo.

timedatectl set-timezone Etc/UTC

Para una consulta puntual no hace falta tocar la zona horaria del sistema: basta con la variable TZ, que actúa solo sobre esa llamada:

TZ=Europe/Vienna date
TZ=Europe/Vienna journalctl -u nginx --since "today"

Paso 2: elegir el servicio de tiempo

Para Debian y Ubuntu hay tres candidatos que resuelven la misma tarea con un alcance muy distinto.

ServicioQué sabe hacerCuándo encaja
systemd-timesyncdCliente puro según SNTP, consulta a un solo servidor cada vez y cambia a otro si este fallaEl caso normal para un servidor único
chronyCliente y servidor NTP completo, combina varias fuentes, trae con chronyc una herramienta de diagnóstico y domina NTSCuando quieres demostrar precisión o buscar fallos
ntpsecSucesor de la implementación de referencia ntpdSistemas heredados y casos especiales

Los tres declaran al sistema de paquetes el mismo papel: Provides: time-daemon y, al mismo tiempo, Conflicts: time-daemon. Si pides dos de ellos en un mismo comando, recibes una negativa:

The following packages have unmet dependencies:
 chrony : Conflicts: time-daemon
 systemd-timesyncd : Conflicts: time-daemon
E: Unable to correct problems, you have held broken packages.

Si instalas chrony mientras timesyncd está en marcha, no pases por alto la línea The following packages will be REMOVED: systemd-timesyncd. Es intencionado: dos procesos sobre el mismo reloj son peores que ninguno. Comprueba justo después si el nuevo servicio está en marcha.

Dos nombres de paquete que aparecen en las guías antiguas ya están fuera de juego: ntp y ntpdate. En Debian 12 y en las dos versiones de Ubuntu son solo paquetes de transición hacia ntpsec; en Debian 13, apt-cache policy ntp responde simplemente Candidate: (none).

Variante A: systemd-timesyncd

En los cuatro sistemas, el paquete systemd recomienda systemd-timesyncd | time-daemon. Una instalación normal trae por tanto el servicio; una imagen mínima sin recomendaciones, no.

DEBIAN_FRONTEND=noninteractive apt-get install -y systemd-timesyncd
systemctl enable --now systemd-timesyncd

El archivo /etc/systemd/timesyncd.conf que viene de serie contiene únicamente valores predeterminados comentados. Tus propios valores van en un archivo complementario, cuyo directorio no existe de fábrica:

mkdir -p /etc/systemd/timesyncd.conf.d
cat > /etc/systemd/timesyncd.conf.d/10-kernelhost.conf <<'EOF'
[Time]
NTP=0.at.pool.ntp.org 1.at.pool.ntp.org 2.at.pool.ntp.org
FallbackNTP=0.pool.ntp.org 1.pool.ntp.org
EOF
systemctl restart systemd-timesyncd

En NTP= van los servidores a los que se consulta. FallbackNTP= solo entra en juego cuando ninguno de ellos responde; de fábrica ahí figura en Debian el pool de Debian y en Ubuntu ntp.ubuntu.com.

Comprobación:

systemd-analyze cat-config systemd/timesyncd.conf
systemctl is-active systemd-timesyncd
timedatectl timesync-status

El primer comando muestra la configuración compuesta, y tu archivo complementario tiene que aparecer ahí con la ruta completa. El tercero indica el servidor utilizado, el intervalo de consulta y el último desfase medido. Si en vez de eso responde con Failed to query server: Connection timed out, timesyncd no está en marcha. Ese mensaje tarda 25 segundos en aparecer.

Variante B: chrony

apt-get install -y chrony
systemctl enable --now chrony

En las cuatro distribuciones la unit se llama chrony.service y lleva además el alias chronyd.service. El archivo /etc/chrony/chrony.conf que viene de serie ya trae valores utilizables: Debian consulta pool 2.debian.pool.ntp.org iburst, Ubuntu consulta ntp.ubuntu.com más tres entradas del pool de Ubuntu. No modifiques ese archivo. A través de confdir /etc/chrony/conf.d incluye un directorio que las actualizaciones de paquetes dejan intacto:

cat > /etc/chrony/conf.d/10-kernelhost.conf <<'EOF'
pool 0.at.pool.ntp.org iburst
EOF
systemctl restart chrony

Comprobación con chronyc tracking; así se ve en un servidor que funciona bien:

Reference ID    : A29FC801 (time.cloudflare.com)
Stratum         : 4
Ref time (UTC)  : Thu Sep 03 17:53:29 2026
System time     : 0.000135970 seconds fast of NTP time
Last offset     : +0.000042723 seconds
RMS offset      : 0.000086483 seconds
Frequency       : 13.967 ppm fast
Residual freq   : +0.002 ppm
Skew            : 0.073 ppm
Root delay      : 0.008282715 seconds
Root dispersion : 0.000613841 seconds
Update interval : 1038.4 seconds
Leap status     : Normal

En el día a día bastan tres líneas. System time es la desviación actual, aquí unos 136 microsegundos. Leap status tiene que decir Normal; con Not synchronised, chrony todavía no tiene ninguna fuente utilizable. Stratum indica a qué distancia estás de un reloj de referencia, y de 2 a 4 es lo normal. La segunda mirada va a las fuentes en sí, con chronyc -n sources:

MS Name/IP address         Stratum Poll Reach LastRx Last sample
===============================================================================
^- 217.175.198.239               3  10   377   748   +114us[ +153us] +/-   19ms
^- 46.102.157.67                 2  10   377   558   +151us[ +192us] +/-   28ms
^* 162.159.200.1                 3  10   377   335   -251us[ -208us] +/- 4463us
^+ 152.53.132.244                2   9   377   231   +335us[ +335us] +/- 5626us

Los dos caracteres del extremo izquierdo son el diagnóstico de verdad. ^* marca la fuente según la cual se ajusta el reloj, ^+ una que también entra en el cálculo, ^- una que no se combina, ^? una inalcanzable y ^x una contradictoria. En la columna Reach hay un valor octal sobre las últimas ocho consultas: 377 significa que se respondieron las ocho, 0 significa que no llegó ninguna.

Desde la versión 4, chrony domina Network Time Security, es decir, hora autenticada por TLS. Eso exige dos cosas que se pasan por alto con facilidad: el paquete ca-certificates y el puerto TCP 4460 saliente. Si faltan los certificados raíz, la sincronización falla con Error in the certificate verification. The certificate is NOT trusted. The certificate issuer is unknown.

apt-get install -y ca-certificates
cat > /etc/chrony/conf.d/20-nts.conf <<'EOF'
server time.cloudflare.com iburst nts
EOF
systemctl restart chrony
chronyc authdata

El reloj hardware

Además de la hora del sistema hay un segundo reloj: el reloj hardware, RTC para abreviar. Al arrancar entrega el primer valor de hora, mucho antes de que se ponga en marcha un servicio de tiempo. En un servidor root KVM es el reloj que proporciona la capa de virtualización, y en timedatectl aparece en la línea RTC time. Importa exactamente un ajuste, la última línea de la salida:

timedatectl set-local-rtc 0
timedatectl | grep "RTC in local TZ"

Si ahí pone yes, el reloj hardware se lee como hora local. Es una concesión a los equipos que arrancan Windows en paralelo y en un servidor no tiene ningún sentido. El daño se ve dos veces al año: en el cambio de hora, un reloj hardware llevado como hora local no es unívoco durante una hora, y un reinicio dentro de esa ventana puede arrancar el servidor con una hora de diferencia.

En la dirección contraria: chrony escribe con regularidad la hora corregida de vuelta en el reloj hardware mediante el ajuste predeterminado rtcsync, y esa línea está en el chrony.conf de serie de las cuatro distribuciones. systemd-timesyncd no tiene ninguna opción parecida. A mano se hace con hwclock --systohc. En Debian 12, Debian 13 y Ubuntu 24.04 ese comando viene del paquete util-linux-extra; si la shell responde hwclock: command not found, instálalo. En Ubuntu 22.04 pertenece a util-linux.

Paso 3: demostrar que la sincronización funciona

Que un comando se ejecute sin errores no demuestra nada. Estas cinco comprobaciones, juntas, sí:

  1. El estado general. timedatectl muestra System clock synchronized: yes y NTP service: active. Las dos líneas juntas, no una sola.
  2. El servicio en sí. systemctl is-enabled chrony y systemctl is-active chrony responden enabled y active, y con timesyncd lo equivalente. enabled por sí solo únicamente significa que arrancaría en el próximo inicio.
  3. Se alcanza de verdad una fuente. Con chrony, en chronyc -n sources tiene que aparecer al menos una línea con ^*, y la columna Reach debe haberse movido de 0. Con timesyncd, timedatectl timesync-status indica un servidor concreto.
  4. El desfase es pequeño. chronyc tracking debería mostrar en System time microsegundos o milisegundos. Segundos enteros significan que la corrección sigue en curso o que algo está atascado.
  5. Sobrevive a un reinicio. Reinicia el servidor una vez y repite las comprobaciones 1 a 4. Es la única comprobación que responde a la pregunta de forma definitiva.

La línea de la que no debes fiarte por sí sola: System clock synchronized: yes refleja un indicador del kernel que puso el proceso que ajustó el reloj por última vez. No desaparece cuando el servicio de tiempo se cae. Un servidor puede mostrar esa línea y llevar horas funcionando sin sincronizarse.

Los servicios que al arrancar necesitan sí o sí una hora correcta los ordenas con After=time-sync.target. Para que ese objetivo se alcance solo después de la sincronización hace falta además una unit que espere: systemd-time-wait-sync.service con timesyncd y chrony-wait.service con chrony. Esta última la traen Debian 12, Debian 13 y Ubuntu 24.04, pero Ubuntu 22.04 no. Cómo se estructura una unit propia lo tienes en crear un servicio systemd.

Firewall y red

NTP sale hacia fuera por el puerto UDP 123, y para NTS se suma el TCP 4460. En la configuración predeterminada de UFW el tráfico saliente está permitido, así que no hay nada que hacer. Si has puesto ufw default deny outgoing, tienes que abrir el puerto; si no, el reloj se queda parado sin que ningún servicio escriba un mensaje de error:

ufw allow out 123/udp comment 'NTP'

El resto sobre el firewall está en configurar el firewall UFW. En sentido contrario vale lo siguiente: un servicio de tiempo que responde a peticiones llegadas de internet es un amplificador para ataques de reflexión. chrony y systemd-timesyncd no responden de fábrica a nadie, y solo una línea allow en la configuración de chrony convierte al cliente en servidor. Ponla acotada únicamente a tu propia red. El filtrado previo del centro de datos maincubes en Frankfurt am Main intercepta ese tráfico, pero el mejor amplificador sigue siendo el que no existe.

Cuando el reloj está muy desviado

Los servicios de tiempo no corrigen normalmente a saltos, sino acelerando o frenando el reloj de forma mínima. Por eso una desviación de un minuto no desaparece en un segundo, y está bien que sea así: un salto hacia atrás hace que las marcas de tiempo aparezcan dos veces.

Para sistemas recién arrancados, el chrony.conf de serie de las cuatro distribuciones trae la línea makestep 1 3: permite un salto real cuando el desfase supera un segundo, y solo durante las tres primeras sincronizaciones. Ese caso se da justo en una máquina virtual clonada de una imagen o restaurada desde un snapshot. Si con eso no basta, fuerzas el salto y esperas al resultado:

chronyc makestep
chronyc waitsync 10

Medir sin cambiar nada se hace con el conmutador -Q: consulta las fuentes configuradas, informa de la desviación y termina sin tocar el reloj. La línea interesante dice entonces algo como System clock wrong by 0.000363 seconds (ignored):

chronyd -Q -f /etc/chrony/chrony.conf

Lo que no deberías hacer es ajustar el reloj a mano con date -s mientras hay un servicio de tiempo en marcha. timedatectl set-time lo rechaza de forma expresa con Failed to set time: Automatic time synchronization is enabled, porque si no habría dos instancias moviendo el mismo reloj a la vez.

Errores frecuentes y soluciones

Failed to set time zone: Invalid or not installed time zone 'Europe/Wien': ese nombre de zona no existe. La base de datos IANA usa nombres de ciudad en inglés, así que es Europe/Vienna, Europe/Zurich y Europe/Prague. Si el mismo mensaje aparece con un nombre bien escrito, lo que falta es la base de datos de zonas: la contraprueba es timedatectl list-timezones | wc -l, y si queda una sola línea, instala tzdata.

Failed to set ntp: NTP not supported: no hay ningún servicio de tiempo instalado, y timedatectl show -p CanNTP --value devuelve entonces no. El conmutador set-ntp solo controla servicios que se han registrado en /usr/lib/systemd/ntp-units.d/: timesyncd con 80-systemd-timesync.list y chrony con 50-chrony.list.

timedatectl set-ntp true se ejecuta sin errores y aun así NTP service sigue en inactive: el comando solo informa de que ha dado la orden a la unit, no de que esta funcione. La causa está en el journal, por ejemplo con journalctl -u systemd-timesyncd -n 20.

506 Cannot talk to daemon: chronyc no alcanza el servicio porque chronyd no está en marcha. systemctl status chrony dice el motivo. El mensaje es idéntico en cualquier subcomando de chronyc.

chrony.service - chrony, an NTP client/server was skipped because of an unmet condition check (ConditionCapability=CAP_SYS_TIME).: el sistema no tiene permitido ajustar el reloj. En los VPS basados en contenedores (LXC, OpenVZ) eso es lo normal, porque ahí la hora la marca el sistema anfitrión. En un servidor root KVM el invitado sí puede ajustar su propio reloj, así que ahí el mensaje es un hallazgo real. Desde el punto de vista de timesyncd, el mismo caso se ve así: systemd-timesyncd.service - Network Time Synchronization was skipped because of an unmet condition check (ConditionVirtualization=!container).

Release file for ... is not valid yet en apt update: el reloj se atrasa. Corrige primero la hora y repite después apt update. Al revés, is expired apunta a un reloj adelantado y, con menos frecuencia, a un mirror desactualizado.

Failed to query server: Connection timed out en timedatectl timesync-status: timesyncd no está en marcha, o chrony lo ha sustituido. Si chrony es el servicio activo, ese comando se queda sin respuesta de forma permanente, y eso no es ningún fallo. La herramienta correcta se llama entonces chronyc tracking.

Diferencias entre distribuciones de un vistazo

PuntoDebian 13Debian 12Ubuntu 24.04Ubuntu 22.04
Versión de chrony4.6.14.34.54.2
Fuente en chrony.confPool de DebianPool de Debianntp.ubuntu.com más pool de Ubuntuntp.ubuntu.com más pool de Ubuntu
/etc/timezoneya no existeexisteexisteexiste
hwclock del paqueteutil-linux-extrautil-linux-extrautil-linux-extrautil-linux
chrony-wait.serviceno
ntp y ntpdateya no existenPaquetes de transiciónPaquetes de transiciónPaquetes de transición

En cambio, hay algo que es igual en todas partes: la zona horaria depende únicamente del symlink /etc/localtime, y cada servicio de tiempo excluye a todos los demás.

La versión corta

Para un servidor root recién aprovisionado que debe funcionar en hora local y al que le basta con el servicio estándar:

DEBIAN_FRONTEND=noninteractive apt-get install -y systemd-timesyncd tzdata
timedatectl set-timezone Europe/Vienna
timedatectl set-local-rtc 0
systemctl enable --now systemd-timesyncd
timedatectl

Al final se esperan Time zone: Europe/Vienna, System clock synchronized: yes, NTP service: active y RTC in local TZ: no. Si esas cuatro líneas siguen así también después de un reinicio, el reloj de ese servidor está resuelto.

Preguntas frecuentes

¿Qué se rompe cuando el reloj de un servidor va mal?
Lo que llama la atención nunca es el reloj, sino una consecuencia suya. Todo certificado TLS tiene una ventana de validez: si la hora del sistema queda por debajo, curl avisa con "curl: (60) SSL certificate problem: certificate is not yet valid"; si queda por encima, con "certificate has expired". apt rechaza los archivos Release con "Release file for ... is not valid yet", y el servidor deja entonces de recibir actualizaciones de seguridad sin que se caiga ningún servicio. A eso se suman logs que ya no se pueden leer unos contra otros, cron jobs a la hora equivocada, backups nombrados por la fecha que sobrescriben el de ayer y las contraseñas de un solo uso basadas en tiempo. TOTP trabaja en ventanas de 30 segundos con una tolerancia de una ventana a cada lado en la mayoría de las implementaciones, así que un reloj de servidor más desviado que eso te deja fuera de verdad.
¿Cambia la hora del servidor si cambio la zona horaria?
No. El kernel lleva exactamente un contador, segundos desde el 1 de enero de 1970 en UTC, y la zona horaria es solo una cuestión de representación. El comando date -u devuelve la misma salida antes y después del cambio. Al revés vale lo mismo: una sincronización cambia el contador, no la representación, así que una zona mal puesta sigue estando mal. Son dos tareas con dos comprobaciones. Ten en cuenta además que cron, las bases de datos y los servidores de aplicaciones leen la zona horaria al arrancar y siguen escribiendo sus logs en la zona antigua hasta que los reinicias.
¿Por qué timedatectl responde "Failed to set time zone: Invalid or not installed time zone"?
Hay dos causas posibles. O bien el nombre no existe: la base de datos IANA usa nombres de ciudad en inglés, así que es Europe/Vienna y no Europe/Wien, igual que Europe/Zurich y Europe/Prague. O bien falta la base de datos de zonas, algo habitual en imágenes reducidas, sobre todo en Ubuntu. La contraprueba es timedatectl list-timezones | wc -l: si queda una sola línea, instala tzdata, y después tienen que salir varios cientos. Por cierto, lo que manda para la zona es únicamente el symlink /etc/localtime, no el archivo /etc/timezone, que Debian 13 ya ni siquiera incluye.
systemd-timesyncd o chrony: ¿qué servicio de tiempo elijo?
systemd-timesyncd es un cliente puro según SNTP, consulta a un solo servidor cada vez y cambia a otro si este falla. Es el caso normal para un servidor único. chrony es un cliente y servidor NTP completo, combina varias fuentes, trae con chronyc una herramienta de diagnóstico y domina Network Time Security, es decir, hora autenticada por TLS. Usa chrony cuando quieras demostrar precisión o buscar fallos. Los dos a la vez no es posible: cada servicio de tiempo declara al sistema de paquetes "Provides: time-daemon" y al mismo tiempo "Conflicts: time-daemon". Por eso instalar chrony elimina systemd-timesyncd, algo visible en la línea "The following packages will be REMOVED: systemd-timesyncd". Comprueba justo después si el nuevo servicio está en marcha.
timedatectl set-ntp falla con "Failed to set ntp: NTP not supported". ¿Qué falta?
No hay instalado ningún servicio de tiempo. En la salida de timedatectl, la línea NTP service muestra entonces el valor n/a, y timedatectl show -p CanNTP --value devuelve no. El conmutador set-ntp solo controla servicios que se han registrado en /usr/lib/systemd/ntp-units.d/: timesyncd con 80-systemd-timesync.list y chrony con 50-chrony.list. Instala uno de los dos. Hay que distinguirlo de NTP service: inactive, donde sí existe un servicio pero no está en marcha, y la causa está en el journal.
chrony no arranca en mi VPS y avisa de una condición no cumplida. ¿Es un fallo?
En el journal aparece entonces "was skipped because of an unmet condition check (ConditionCapability=CAP_SYS_TIME)", es decir, el sistema no tiene permitido ajustar el reloj. En los VPS basados en contenedores (LXC, OpenVZ) eso es lo normal, porque ahí la hora la marca el sistema anfitrión. En un servidor root KVM el invitado sí puede ajustar su propio reloj, así que ahí el mensaje es un hallazgo real. Desde el punto de vista de timesyncd, el mismo caso aparece con la condición ConditionVirtualization=!container.
¿Cómo demuestro que la sincronización horaria funciona de verdad?
La línea System clock synchronized: yes no basta por sí sola. Refleja un indicador del kernel que puso el proceso que ajustó el reloj por última vez, y no desaparece cuando el servicio de tiempo se cae. Comprueba por eso cinco cosas: que al lado ponga NTP service: active; que systemctl is-enabled e is-active respondan enabled y active para el servicio; que se alcance de verdad una fuente (con chrony, una línea con ^* en chronyc -n sources y una columna Reach distinta de 0; con timesyncd, un servidor concreto en timedatectl timesync-status); que chronyc tracking muestre microsegundos o milisegundos en System time; y que todo sobreviva a un reinicio. Solo la última comprobación responde a la pregunta de forma definitiva.
¿El servidor debe funcionar en UTC o en hora local?
Si gestionas varios servidores o varias ubicaciones, usa UTC; si llevas un único servidor según el reloj de la oficina, usa la hora local. A favor de UTC juega que no conoce el horario de verano: la noche del cambio, las 02:30 en hora local existen dos veces en otoño y ninguna en primavera, lo que afecta a cualquier job dentro de esa ventana. En contra está que son personas las que leen los logs y que una ventana de mantenimiento los domingos a las 03:00 se refiere a la hora local. Lo único incorrecto es no saberlo. Para una consulta puntual no hace falta tocar la zona horaria del sistema, TZ=Europe/Vienna date actúa solo sobre esa llamada. Con independencia de esa decisión, el reloj hardware va en UTC: timedatectl set-local-rtc 0, y en la salida tiene que poner RTC in local TZ: no.

Zona horaria NTP timedatectl chrony systemd-timesyncd Linux Debian Ubuntu