SSD Nodes Learn 🎉 VPS desde $5.50/mes
Guías Matt ConnorPor Matt Connor

Zonas horarias y activadores de programación en n8n

n8n usa TZ, GENERIC_TIMEZONE y la zona del flujo. Corrige las tres para evitar que el activador de programación se ejecute a una hora incorrecta.

Por qué el activador de programación de n8n se ejecuta a una hora incorrecta

Un activador de programación de n8n se ejecuta a una hora incorrecta porque n8n lee la zona horaria desde tres lugares distintos, y corregir uno solo resuelve únicamente una parte del problema. Esos tres lugares son la variable TZ del contenedor, la configuración predeterminada de la instancia GENERIC_TIMEZONE y la zona horaria definida dentro de un flujo de trabajo individual. Configure los tres una sola vez y todas las programaciones que cree después se ejecutarán a la hora esperada.

Primero, corrija una suposición habitual. Una instalación nueva de n8n autohospedado no programa las ejecuciones en UTC (hora universal coordinada). El reloj del contenedor está en UTC porque la imagen oficial no define TZ. La programación es una capa independiente, y el valor predeterminado documentado de n8n para GENERIC_TIMEZONE es America/New_York (en agosto de 2026); por eso una instancia sin modificar ejecuta sus activadores de programación según la hora de Nueva York. Por ese motivo, el desfase que se observa rara vez coincide con la diferencia de cada usuario respecto a UTC. Un administrador en Berlín que solicita las 06:00 obtiene las 12:00 hora local, y las 11:00 durante las semanas de marzo en las que Estados Unidos ya ha cambiado al horario de verano y Europa todavía no.

Las tres capas de zona horaria y cuál tiene prioridad

TZ es la zona horaria del sistema operativo dentro del contenedor. La documentación de n8n la describe como la variable que establece la zona horaria del sistema para controlar lo que devuelven los scripts y comandos como date. Determina lo que muestra date dentro del contenedor, la marca de tiempo que aparece en una línea del registro del contenedor, lo que devuelve new Date() en un nodo Code y lo que ve cualquier script de shell que ejecute allí. No afecta a la hora a la que se ejecuta un Schedule Trigger.

GENERIC_TIMEZONE es la zona horaria de la instancia de n8n. La documentación la denomina zona horaria de la instancia de n8n y señala que es importante para los nodos de programación, como Cron. Aquí, Cron se refiere a la sintaxis estándar de programación basada en tiempo, que n8n expone como la opción Custom (Cron) en Schedule Trigger.

La zona horaria del workflow se configura para cada workflow. Abra el workflow en el lienzo, seleccione los tres puntos de la esquina superior derecha, seleccione Settings y cambie el valor de Timezone. Esto reemplaza GENERIC_TIMEZONE sólo para ese workflow.

En un Schedule Trigger, el orden es fijo. n8n usa la zona horaria del workflow si el workflow tiene una; en caso contrario, usa la zona horaria de la instancia definida en GENERIC_TIMEZONE; si tampoco está definida, usa el valor predeterminado integrado America/New_York. TZ no se consulta en ningún paso de esta decisión.

Para las fechas dentro de los nodos, la respuesta depende del reloj que consulte el código. Luxon, la biblioteca de fechas que utilizan las expresiones de n8n, usa la zona horaria de n8n. Por tanto, $now y $today siguen el mismo orden workflow-then-instance que el trigger. El new Date() de JavaScript estándar en un nodo Code consulta el sistema operativo, por lo que sigue TZ. Esta separación es la causa de la mayor parte de la confusión: el trigger puede funcionar correctamente mientras todas las marcas de tiempo que escribe el workflow aparecen desplazadas varias horas.

Establezca los tres en el archivo Compose

Coloque TZ y GENERIC_TIMEZONE juntos en el archivo para evitar establecer uno y olvidar el otro. El fragmento siguiente contiene la parte relacionada con la zona horaria de un servicio operativo. El resto del archivo, el proxy inverso y el certificado, procede de un n8n autohospedado en una VPS detrás de HTTPS.

services:
  n8n:
    image: docker.n8n.io/n8nio/n8n
    restart: unless-stopped
    ports:
      - "127.0.0.1:5678:5678"
    environment:
      - GENERIC_TIMEZONE=Europe/Berlin
      - TZ=Europe/Berlin
      - N8N_RUNNERS_ENABLED=true
      - N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
    volumes:
      - n8n_data:/home/node/.n8n

volumes:
  n8n_data:

Aplíquelo con docker compose up -d, no con docker compose restart. Un reinicio vuelve a iniciar el mismo contenedor con el entorno con el que se creó, por lo que cambian los archivos, pero no el proceso en ejecución. up -d detecta el entorno modificado y vuelve a crear el contenedor. Si mantiene estos valores en un archivo env en lugar de declararlos directamente, se aplica la misma regla de recreación. La guía Gestión de archivos env y secretos en Compose explica desde dónde se lee ese archivo.

Use un nombre de zona de IANA (Internet Assigned Numbers Authority) con el formato Region/City, como Europe/Berlin o America/Sao_Paulo. Esos nombres incluyen las reglas de horario de verano de cada lugar, por lo que el desfase cambia cuando cambia la hora local. Un nombre con desfase fijo, como Etc/GMT+5, nunca cambia con las estaciones y su signo está invertido respecto a lo que se podría deducir. Ejecute LC_ALL=C TZ=Etc/GMT+5 date +%z para que muestre -0500. Evite esos nombres.

Por qué configurar sólo uno deja la corrección a medias

Configure GENERIC_TIMEZONE por separado y Schedule Trigger se ejecutará a la hora prevista, mientras todo lo que consulta el sistema operativo seguirá usando UTC. Un nodo Code que llame a new Date().toString() devolverá una cadena UTC, las líneas del registro del contenedor llevarán marcas de tiempo en UTC y cualquier nombre de archivo generado a partir del reloj del sistema cambiará de día a la medianoche incorrecta.

Configure TZ por separado y ocurrirá lo contrario. docker compose exec n8n date mostrará la hora local, lo que parecerá indicar que todo funciona, mientras Schedule Trigger seguirá usando America/New_York y se ejecutará seis horas antes o después de la hora solicitada. Esta configuración es la que más tiempo hace perder, porque la comprobación que la mayoría ejecuta primero es precisamente la que ahora funciona.

Configure una zona horaria para un workflow y cambie después GENERIC_TIMEZONE. Ese workflow ignorará el cambio. Prevalece el valor del workflow y seguirá prevaleciendo hasta que alguien abra la configuración de ese workflow. Si un workflow se ejecuta a una hora extraña mientras los workflows vecinos funcionan correctamente, casi siempre la causa es esta.

Compruebe los relojes en lugar de hacer suposiciones

Compare directamente el host y el contenedor.

date
docker compose exec n8n date
docker compose exec n8n printenv TZ GENERIC_TIMEZONE

Los dos primeros comandos deben mostrar la misma hora del sistema cuando TZ esté configurado. printenv muestra una línea por cada variable existente, por lo que dos líneas de salida indican que ambas están configuradas y una línea indica que está observando el estado parcialmente corregido.

A continuación, consulte el propio n8n desde un workflow, porque un shell del contenedor no puede mostrarle cuál es la zona horaria del nivel del workflow. Añada un nodo Code al workflow que presenta el problema y ejecútelo una vez con Execute Workflow.

return [
  {
    json: {
      n8n_time: $now.toISO(),
      n8n_zone: $now.zoneName,
      system_time: new Date().toString(),
    },
  },
];

n8n_zone es la zona que usará el Schedule Trigger de este workflow, ya resuelta según el orden workflow y después instancia, por lo que responde directamente a la pregunta. system_time contiene la zona propia del contenedor, obtenida de TZ. Ejecútelo en el workflow que presenta el problema y no en uno nuevo, porque la configuración del nivel del workflow se conserva con el workflow. Si esos dos valores no coinciden, habrá encontrado el problema sin abrir un solo archivo de configuración.

Expresiones de Cron en el nodo Schedule Trigger

Schedule Trigger ofrece intervalos fijos desde segundos hasta meses, además de la opción Custom (Cron) para cualquier intervalo que no cubran esas opciones. La expresión de cron se interpreta en la zona horaria efectiva del workflow, por lo que 0 6 * * * significa las 06:00 en esa zona, no las 06:00 UTC. Una expresión de cinco campos de crontab guru se puede pegar tal cual. n8n también acepta un campo opcional para los segundos, que la tabla de campos de la documentación coloca al principio: segundo, minuto, hora, día del mes, mes y día de la semana.

No codifique nunca el desfase manualmente. Escribir 0 4 * * * en una instancia con zona UTC para ejecutar la tarea a las 06:00 en Berlín es correcto en invierno y queda adelantado una hora durante todo el verano, porque Berlín usa UTC+1 en invierno y UTC+2 en verano. Configure la zona y escriba la hora local que realmente necesita.

Qué hace el horario de verano con una tarea programada a las 02:30

Una hora local de reloj no es un instante garantizado. Dos veces al año desaparece una hora y otra se repite, y cualquier tarea programada dentro de esas horas se ve afectada. Puede observarlo con date en cualquier equipo Linux, sin usar n8n.

LC_ALL=C TZ=Europe/Berlin date -d '2027-03-28 02:30'
date: invalid date '2027-03-28 02:30'

No es un error tipográfico en el comando. El 2027-03-28, el reloj de Berlín pasa directamente de las 02:00 a las 03:00, por lo que las 02:30 locales no existen ese día y date se niega a convertirlas en un instante. Una tarea fijada a las 02:30 locales no tiene ningún momento en el que ejecutarse. Las horas contiguas son válidas: date -d '2027-03-28 01:30' se resuelve como CET y date -d '2027-03-28 03:30' se resuelve como CEST.

La transición de otoño es el caso inverso. El 2027-10-31, el reloj de Berlín retrocede de las 03:00 a las 02:00, por lo que las 02:30 ocurren dos veces.

LC_ALL=C TZ=Europe/Berlin date -d '2027-10-31 02:30 CEST' '+%s'
LC_ALL=C TZ=Europe/Berlin date -d '2027-10-31 02:30 CET' '+%s'
1824942600
1824946200

Son dos instantes distintos, ambos llamados 02:30 local, separados por 3600 segundos. Una tarea fijada a esa hora puede ejecutarse dos veces o una sola vez a una hora que nadie eligió, y ninguno de los dos resultados es adecuado para una ejecución de facturación o una rotación de copias de seguridad. Mueva la programación fuera de ese intervalo. En la mayoría de las zonas de Europa y Norteamérica, la franja de riesgo va de las 00:00 a las 03:00 locales.

Programar la infraestructura en UTC y mostrar la hora local a las personas

La respuesta estándar separa las dos funciones de una zona horaria. Las máquinas necesitan un intervalo estable. Las personas necesitan una hora fácil de leer.

  • Para las tareas que nadie supervisa, configure la zona horaria del flujo de trabajo en UTC. Las copias de seguridad, la precarga de caché, el envío de registros y la generación de informes pertenecen a esta categoría. En UTC, el intervalo entre dos ejecuciones es exactamente el intervalo que haya definido todos los días del año, porque UTC no aplica horario de verano.
  • Para las tareas cuyos resultados lee una persona, mantenga la programación en UTC y convierta la hora al mostrarla. Una expresión lo hace: {{ $now.setZone('Europe/Berlin').toFormat('yyyy-MM-dd HH:mm') }} inserta la hora local en el cuerpo del mensaje, mientras el activador permanece estable.

La misma separación se aplica fuera de n8n. Cuando una parte de la automatización se ejecuta como un servicio y un temporizador de systemd en el VPS, su línea OnCalendar se interpreta según la zona horaria del sistema, que es un cuarto reloj con su propia configuración. Mantener todos los planificadores en UTC le deja una sola regla que recordar en lugar de cuatro. Esto también importa para cualquier tarea que resuma un periodo, porque un flujo de trabajo de agente de IA de n8n al que se le soliciten las cifras de ayer usará, sin indicarlo, 24 horas diferentes según la zona que se haya aplicado.

Modos de fallo y salida que verá

Todo se ejecuta con unas seis horas de diferencia. GENERIC_TIMEZONE nunca se estableció, por lo que se aplica el valor predeterminado integrado America/New_York. docker compose exec n8n printenv GENERIC_TIMEZONE no muestra ninguna salida. Establézcalo y vuelva a crear el contenedor.

Editó el archivo Compose y no cambió nada. Ejecutó docker compose restart, por lo que el contenedor conservó su entorno original. Ejecute docker compose up -d y confirme el valor con docker compose exec n8n printenv TZ.

El activador es correcto, pero las marcas de tiempo son incorrectas. Sólo está establecido GENERIC_TIMEZONE. Un new Date() en un nodo Code sigue leyendo la zona horaria UTC del sistema operativo. Establezca TZ con el mismo valor y vuelva a crear el contenedor.

Un flujo de trabajo ignora la configuración de la instancia. Ese flujo tiene su propia zona horaria en la configuración, que tiene prioridad sobre GENERIC_TIMEZONE. Abra el lienzo, seleccione los tres puntos, Settings y Timezone.

Un trabajo diario se ejecutó dos veces o se omitió un día una vez este año. La hora programada está dentro de una transición al horario de verano. Cambie la hora o cambie ese flujo de trabajo a UTC.

FAQ

¿Por qué mi activador de programación de n8n se ejecuta a una hora incorrecta?

El workflow está resolviendo una zona horaria distinta de la que supone. n8n selecciona la zona horaria del workflow si tiene una configurada; en caso contrario, usa la zona horaria de la instancia definida en GENERIC_TIMEZONE y, si tampoco existe, usa su valor predeterminado integrado de America/New_York. Una instancia autohospedada en la que nadie haya definido GENERIC_TIMEZONE programa las tareas con la hora de Nueva York, no con UTC. Por eso el desfase rara vez coincide con su propia diferencia respecto a UTC. Ejecute docker compose exec n8n printenv GENERIC_TIMEZONE. Si no muestra ninguna salida, nunca se ha definido.

¿Cuál es la diferencia entre TZ y GENERIC_TIMEZONE en n8n?

TZ es la zona horaria del sistema operativo dentro del contenedor. Determina lo que devuelve date dentro del contenedor, las marcas de tiempo que aparecen en las líneas del registro del contenedor, lo que devuelve new Date() en un nodo Code y lo que detecta cualquier script que ejecute allí. GENERIC_TIMEZONE es la zona horaria de la instancia de n8n, y es la que usan los nodos de programación y las expresiones de Luxon como $now. Si configura una sin configurar la otra, tendrá un activador correcto con marcas de tiempo incorrectas, o marcas de tiempo correctas con un activador que se ejecuta varias horas fuera de la hora esperada. Configure ambas con el mismo valor.

¿Debo configurar la zona horaria del workflow o GENERIC_TIMEZONE?

Configure GENERIC_TIMEZONE como valor predeterminado para toda la instancia y use la configuración por workflow sólo cuando un workflow pertenezca realmente a otra zona. El valor del workflow tiene prioridad sobre el valor de la instancia y no se actualiza con cambios posteriores en GENERIC_TIMEZONE. Por eso, un valor sobrescrito por workflow que se haya olvidado resulta difícil de localizar meses después.

¿Qué ocurre con una tarea programada a las 02:30 cuando cambia la hora?

Esa hora local puede desaparecer o producirse dos veces. LC_ALL=C TZ=Europe/Berlin date -d '2027-03-28 02:30' devuelve date: invalid date '2027-03-28 02:30' porque ese día el reloj de Berlín salta de las 02:00 a las 03:00. El 2027-10-31, la misma hora del reloj local corresponde a dos instantes separados por una hora. Evite programar tareas entre las 00:00 y las 03:00 hora local, o configure el workflow con UTC y convierta a la hora local sólo cuando una persona deba leerla.