systemd timer AccuracySec — el desfase que nadie midió
Los timers de systemd se retrasan a propósito: el default de AccuracySec agrupa despertares. Medido: 10 disparos previstos fueron 5; con vecino ocupado, 6 de 6.
systemd timer AccuracySec es la búsqueda que te trae hasta aquí porque tu timer con OnCalendar=*:0/15 sigue disparando en el minuto :07 y journalctl -u dice que todo está bien. Todo está bien — systemd movió tu despertar a propósito. Este post es la versión medida: qué compra el default, qué cuesta, y la única línea que te devuelve el horario.
Para que un timer de systemd dispare exacto a su horario: pon
AccuracySec=1s(o menos) en la sección[Timer]del timer. El default es 1 minuto — systemd alinea cada despertar a una grilla gruesa y dispara hasta 60 s tarde para poder agrupar varios timers en un solo despertar de CPU. Mira el retraso real consystemctl show your.timer -p LastTriggerUSec— se actualiza en cada disparo.
Última prueba 2026-11-14 · systemd 262 (262.1-1-arch) · VM Arch recién creada. Cada timer de abajo corrió de verdad; cada recibo está pegado tal cual de la máquina.
*:*:00/15), mismo timer, dos máquinas. Quieta: diez marcas se agrupan en cinco disparos — la ventana se traga una marca de por medio. Vecino ocupado: cada marca cae en su hora, porque adentro de la ventana ya existe un despertar.¿Por qué mi timer de systemd dispara tarde?
La frase-verdad: un timer de systemd con su default puede disparar hasta 60 segundos después de su hora prevista, y list-timers lo anota como «next elapse» sin quejarse. No es un bug ni un desfase de reloj — es AccuracySec, que viene en 1min por defecto y cuyo único trabajo es dejar que el planificador llegue tarde.
$ sudo systemctl show accA.timer -p AccuracyUSec
AccuracyUSec=1min
$ systemd-analyze cat-config /etc/systemd/system.conf 2>/dev/null | grep -i DefaultTimerAccuracy
#DefaultTimerAccuracySec=1min
verifica: pegado tal cual de la VM de banco — Arch de fábrica, sin config de usuario, así que el timer reporta el default compilado. Fíjate en el nombre de la propiedad: el archivo unit dice AccuracySec=, y systemd 262 la expone como AccuracyUSec.
La mecánica, en un párrafo: cuando vence la hora de disparo de un timer, systemd no despierta la CPU en el acto. Calcula una ventana — desde el momento previsto hasta AccuracySec más tarde — y dispara en el punto de esa ventana que le permite compartir el despertar con otros timers, o estirar el sueño de la CPU. Tu calendario *:0/15 dice «cuatro veces por minuto»; el planificador oye «cuatro chances de compartir viaje» — y en una máquina quieta toma uno, más o menos.
¿Qué tan tarde queda el default, medido?
El experimento, en un párrafo: un timer programado con OnCalendar=*:*:00/15 — cuatro disparos previstos por minuto — con la accuracy de fábrica, corrido dos veces en la misma VM Arch recién creada. Una vez solo en una máquina quieta, otra junto a un timer ocupado de 2 segundos. Cada disparo anota su hora real de ejecución; los recibos son los timestamps crudos.
Primero la máquina quieta — las condiciones en que de verdad viven la mayoría de los timers de limpieza:
accA 1794634092.128787380
accA 1794634125.384544834
accCo 1794634125.384546104 ← co-timer mark was :30 of the previous minute
accA 1794634152.127459587
accCo 1794634186.135416303 ← co-timer mark was :30 of the previous minute
accA 1794634186.135424133
accA 1794634212.133508500
verifica: pasaron 10 marcas y el servicio corrió 5 veces — huecos de 26–34 s (media 30.0) en un calendario que decía cada 15. Contra la marca más cercana, cada disparo cayó 12.1, 0.4, 12.1, 1.1, 12.1 s tarde: la mitad del horario, agrupado en cinco despertares.
Mismo timer, mismo calendario — un vecino ocupado (un timer de 2 segundos, AccuracySec=1us):
accA sched=1794633795101333 mono=193094591 exec=1794633795.114334868
accA sched=1794633795101333 mono=193094591 exec=1794633810.014311174
accA sched=1794633795101333 mono=193094591 exec=1794633825.116389742
⋮ (6 fires, 6 marks — every one on its mark)
verifica: con un despertar ya planeado cada 2 segundos, todos esos despertares caen dentro de la ventana de 60 segundos del timer con default — así que el planificador lo dispara en el primer despertar disponible: en su marca, 119 ms tarde en el peor caso. La holgura no se achicó; el horario del vecino la gastó.
Ese par de recibos es todo el mecanismo en una pantalla: la ventana es una oferta, no una fecha límite — systemd puede disparar en cualquier momento dentro de ella, y dispara en el despertar de CPU más temprano que la ventana cubra. Máquina ocupada: hay un despertar al instante y tu timer parece puntual. Máquina quieta: no hay ningún despertar, así que el planificador programa uno en el borde lejano de la ventana, y cada disparo en cola dentro de esa ventana viaja en él. Las laptops sin uso y los servidores solos — donde corre la mayoría de los reemplazos de cron — son el caso de la máquina quieta. Por eso la misma unit es «en mi desktop va bien, en el NAS se atrasa».
¿Qué cambia de verdad AccuracySec?
Mismo calendario, misma VM, una línea: AccuracySec=100ms. Ahora la ventana de despertar mide una décima de segundo, y el timer cae donde el calendario apuntaba:
accB 1794634230.114374054
accB 1794634245.119370330
accB 1794634260.114446983
verifica: tres disparos, tres marcas — 116 ms tarde en promedio, 119 ms en el peor caso, ventana de 100 ms; los ~15 ms de exceso son la holgura de despacho que carga cualquier VM. Ese es todo el dial: el ancho de la ventana, nada más.
Y el agrupamiento se ve a simple vista en el timer compañero. Programado a las :30 con accuracy por defecto en la máquina ocupada, nunca esperó una ventana completa — disparó 15 ms después de su marca, subido al despertar que el timer de 15 segundos de al lado ya estaba haciendo. Dos timers, un despertar:
accCo sched=1794633810000832 mono=207994091 exec=1794633810.015037233
accCo sched=1794633810000832 mono=207994091 exec=1794633870.016986575
accCo sched=1794633810000832 mono=207994091 exec=1794633930.020077789
(accA exec at the same instants: 1794633795.114, 1794633810.014 …)
verifica: la marca de las :30 de co es también una marca del timer de 15 segundos — el despertar de las :30.000 ya estaba planeado, y co se sumó 15 ms tarde. En la máquina quieta hizo el mismo truco sin ningún vecino de las :30, cayendo sobre el despertar del timer A con 1.2 µs de diferencia (recibo arriba). Misma ventana, mejor abordaje.
«systemd no llega tarde. Va en auto compartido — un despertar por minuto, cuatro pasajeros. AccuracySec es qué tan lleno tiene que venir el auto.»
¿Cómo haces que un timer dispare en su hora?
Una línea de drop-in, sin reiniciar nada más que la unit del timer:
$ sudo systemctl cat accB.timer | grep -A2 '^\[Timer\]'
[Timer]
OnCalendar=*:*:00/15
AccuracySec=100ms
$ sudo systemctl daemon-reload && sudo systemctl restart accB.timer
$ sudo systemctl show accB.timer -p AccuracyUSec,LastTriggerUSec
AccuracyUSec=100ms
LastTriggerUSec=Sat 2026-11-14 05:33:45 UTC
verifica: systemctl show imprime la ventana efectiva — un drop-in le gana al default compilado, y LastTriggerUSec se actualiza en cada disparo. Tu recibo de antes/después es una sola consulta, sin excavar el journal.
Qué tan abajo ir: el timer C corrió con AccuracySec=1us en un calendario de 2 segundos y entregó 11 disparos en 22 s con 23 ms de jitter en el peor de los 11 — la planificación sub-milisegundo existe si la quieres. Pero mira la columna del costo antes de desplegarlo en toda tu flota:
¿Qué te cuesta la precisión?
La máquina quieta sirvió diez marcas con cinco despertares; el timer de 1 µs habría pedido setenta y cinco en la misma ventana. En una laptop, esa es justo la diferencia que el default existe para evitar — el agrupamiento no es flojera, es la misma disciplina de energía que junta los volcados de disco. A los servidores no les importa; a tu batería sí. Escalones sensatos: 1s para fechas límite que un humano nota, 1min (el default) para limpieza que nadie nota, y nada por debajo de 100ms salvo que hayas medido por qué — a ese punto, un servicio de larga vida con un bucle de sleep le gana a una estampida de timers. Y 1us en una laptop es cómo conviertes una batería en un calentador de manos.
El dial, de un vistazo
| AccuracySec | calendario | previstos | disparos | qué pasó |
|---|---|---|---|---|
1min (default) | *:*:00/15, máquina quieta | 10 | 5 | huecos de 26–34 s, media 30.0 — la mitad de los disparos |
1min (default) | *:*:00/15, vecino ocupado | 6 | 6 | cada marca, ≤119 ms tarde |
100ms | *:*:00/15 | 3 | 3 | cada marca, ≤119 ms tarde |
1us | *:*:00/2 | 11 | 11 | cada marca, ≤22.7 ms tarde |
Receta de banco — reproduce estos recibos en cualquier VM
Sirve cualquier timer cuyo servicio anote su propia hora de arranque: ExecStart=/bin/sh -c 'echo timer-run $(date +%%s.%N) >>/var/lib/stamps.log' (escapa el %% en archivos unit). Corre un timer OnCalendar=*:*:00/15 con accuracy de fábrica en una máquina quieta durante dos minutos — cuenta los disparos; luego añade un vecino *:*:00/2 y mira cómo las marcas encajan de golpe. Compara disparo por disparo con systemctl show your.timer -p LastTriggerUSec, que se actualiza en cada disparo. Las variables de entorno TRIGGER_TIMER_REALTIME_USEC/_MONOTONIC_USEC del servicio llevan el timestamp de activación de la unit del timer, no un horario por disparo — no las uses para esto.
Medido: una VM Arch recién creada (systemd 262, 262.1-1-arch, kernel 7.2.11-arch1-1) — el mismo timer *:*:00/15 corrido en reposo y junto a un vecino ocupado, más corridas con accuracy de 100 ms y 1 µs; los recibos son timestamps crudos de date +%s.%N, pegados sin editar. El chequeo del tope de journald en la entrega de la semana pasada, «journalctl: uso de disco — vacuum achica, SystemMaxUse tapa» (2026-11-13), corrió desde un timer igual al caso quieto — así es como este banco se ganó su lugar en el calendario.
BANCO VERIFICADO A MANO · systemd 262 (262.1-1-arch) · VM Arch recién creada · última prueba 2026-11-14
Una línea pone tu horario en orden — AccuracySec=1s. Un recibo lo demuestra — LastTriggerUSec.