SSD Nodes Learn 🎉 VPS desde $4.99/mes
Guías Matt ConnorPor Matt Connor · Actualizado 2026-08-07

Desactivar wp-cron y usar system cron en WordPress

wp-cron depende de visitas y falla en sitios con poco tráfico. Desactívalo, ejecútalo con system cron y WP-CLI, y comprueba que las tareas se ejecutan.

Qué es wp-cron y por qué system cron lo reemplaza

WP-Cron es el programador de tareas integrado en WordPress y sólo se ejecuta cuando alguien solicita una página. WordPress no inicia ninguna tarea por sí solo. En cada solicitud que no se sirve desde una caché, WordPress lee una lista de trabajos programados y, si alguno está pendiente, envía una segunda solicitud HTTP a /wp-cron.php para ejecutar la tarea. Si se traslada ese trabajo a system cron, se obtiene una ejecución predecible con un intervalo fijo, tanto si el sitio ha tenido mil visitantes durante ese minuto como si no ha tenido ninguno.

Dos líneas realizan el trabajo: una constante en wp-config.php y una entrada de crontab. Todo lo demás de esta guía cubre lo que esas dos líneas no indican. Debe determinar con qué usuario debe ejecutarse el trabajo, cómo comprobar que los eventos programados se ejecutaron realmente y cuáles son las tres formas en que la configuración puede fallar sin mostrar ningún mensaje en el sitio.

Los ejemplos usan /srv/www/example.com como directorio de WordPress y www-data como usuario del servidor web. Sustituya estas rutas y este usuario por los valores propios en todos los casos.

Qué visitante provocó el coste de cron en un sitio con mucha carga

Cada solicitud que no proviene de la caché paga el coste de la comprobación. WordPress carga la opción cron, compara las marcas de tiempo y, cuando hay una tarea pendiente, llama a spawn_cron(), que envía una solicitud loopback no bloqueante a /wp-cron.php. El visitante no espera el resultado. Un proceso de PHP sí lo hace. En un VPS pequeño que ejecuta PHP-FPM con pm.max_children = 5, una tarea programada lenta ocupa una quinta parte de la capacidad de PHP durante todo el tiempo que necesita. Además, lo más probable es que se active durante el minuto de mayor carga, porque es cuando se producen más cargas de página.

WordPress limita los duplicados. Adquiere un bloqueo cuya duración es WP_CRON_LOCK_TIMEOUT, 60 segundos de forma predeterminada, para que varios visitantes simultáneos no inicien una ejecución cada uno. El bloqueo limita la duplicación. No saca el trabajo de la ruta de la solicitud.

Cuente con qué frecuencia se ejecuta en su propio servidor antes de decidir si es importante. Cada loopback aparece en el registro de acceso del servidor web:

sudo grep -c 'wp-cron.php' /var/log/nginx/access.log

Apache escribe en /var/log/apache2/access.log en su lugar. Un recuento de miles de ejecuciones al día representa un coste real. Es el tipo de cifra que debe medir en su propio servidor, en lugar de leerla en un artículo, del mismo modo que compararía el rendimiento de un VPS antes y después de cualquier otro cambio.

La caché cambia el resultado. Si una caché de página sirve la mayoría de las solicitudes como HTML estático, PHP nunca se ejecuta para esas solicitudes y la comprobación de cron nunca se produce. Un sitio con mucha carga y una caché eficaz empieza a comportarse como el sitio tranquilo que se muestra a continuación.

Qué visitante activó cron y provocó fallos en un sitio con poco tráfico

Sin visitantes, no hay cron. Un sitio que recibe unas pocas visitas al día ejecuta sus tareas programadas unas pocas veces al día, en los momentos aleatorios en que llegan esas visitas.

Los síntomas son siempre similares. Una entrada programada para las 09:00 permanece en la lista de entradas con la marca Missed schedule hasta que alguien carga una página. Los plugins de copia de seguridad omiten la ejecución nocturna. Las comprobaciones de actualizaciones se retrasan, por lo que el panel no muestra ninguna actualización aunque ya esté disponible una versión de seguridad. Los correos de pedidos, los avisos de renovación y las advertencias de caducidad se envían tarde.

Nada de esto registra un error. Desde el punto de vista de WordPress, la tarea nunca llegó tarde porque nunca se inició.

Paso 1: desactivar el activador de visitantes en wp-config.php

Abra /srv/www/example.com/wp-config.php y añada la constante:

define( 'DISABLE_WP_CRON', true );

Colóquela antes de la línea que contiene /* That's all, stop editing! Happy publishing. */, porque la línea situada justo debajo de ese comentario requiere wp-settings.php, y wp-settings.php es donde WordPress enlaza la comprobación de cron con init. Una constante definida después de ese require se establece demasiado tarde para cambiar nada, y el archivo parece correcto aunque el activador siga ejecutándose.

Confirme que la línea está donde espera:

grep -n "DISABLE_WP_CRON\|stop editing" /srv/www/example.com/wp-config.php

La constante no impide que se programen eventos. Los plugins siguen añadiendo tareas a la cola, exactamente igual que antes. Sólo impide que las cargas de página procesen esa cola, por lo que la cola ya no se ejecutará hasta que termine el paso 3.

Tampoco bloquea las solicitudes directas a /wp-cron.php. Cualquiera puede solicitar esa URL, y normalmente no supone un problema porque el archivo sólo ejecuta las tareas cuyo momento ha llegado. Bloquearla en la configuración del servidor web es opcional. Si la bloquea, el mecanismo alternativo de curl situado cerca del final de esta guía dejará de funcionar.

Paso 2: instalar WP-CLI

WP-CLI es la herramienta oficial de línea de comandos para WordPress. Necesita el binario de línea de comandos de PHP, que es un paquete independiente del módulo de PHP del servidor web.

php -v
sudo apt install -y php-cli

Instale WP-CLI desde la compilación phar, que es la opción recomendada en la guía oficial de instalación:

cd /tmp
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
php wp-cli.phar --info
chmod +x wp-cli.phar
sudo mv wp-cli.phar /usr/local/bin/wp
wp --info

php wp-cli.phar --info muestra la ruta del binario de PHP, la versión de PHP y la versión de WP-CLI. Si muestra los tres valores, el phar funciona. En agosto de 2026, la guía de instalación establece PHP 7.2.24 como versión mínima, y Ubuntu 24.04 incluye PHP 8.3, por lo que un servidor actualizado supera ampliamente ese requisito. Más adelante, actualice con sudo wp cli update.

Ejecute WP-CLI con el usuario del sitio, nunca como root:

sudo -u www-data /usr/local/bin/wp --path=/srv/www/example.com core version

Como root, WP-CLI se niega a iniciarse:

Error: YIKES! It looks like you're running this as root.

Sugiere --allow-root. No lo use aquí. El motivo se explica en el primer modo de fallo siguiente.

Tenga en cuenta también que sudo -u www-data -i no funciona, porque el shell de inicio de sesión de esa cuenta es /usr/sbin/nologin y se obtiene This account is currently not available. Pasar el comando directamente a sudo -u omite el shell de inicio de sesión, por lo que se ejecuta correctamente.

Ahora confirme que el propio WordPress reconoce la constante del paso 1:

sudo -u www-data /usr/local/bin/wp --path=/srv/www/example.com eval 'var_dump( DISABLE_WP_CRON );'

Esto muestra bool(true). Un error fatal sobre una constante no definida significa que no se está llegando a la línea define(), lo que normalmente indica que quedó después de require.

Paso 3: añadir la entrada de cron con el usuario correcto

El usuario correcto es el propietario de los archivos que escribe PHP. Compruebe ambos extremos:

stat -c '%U %G' /srv/www/example.com/wp-content/uploads
grep -E '^(user|group) = ' /etc/php/8.3/fpm/pool.d/www.conf

En una instalación predeterminada de Ubuntu, ambos comandos devuelven www-data. Si asignó al sitio su propio pool de PHP-FPM con su propio usuario, que es el resultado habitual de una configuración por sitio en una pila LAMP en Ubuntu 24.04, use ese usuario en todo lo siguiente.

Cree un directorio de registros en el que ese usuario pueda escribir:

sudo install -d -o www-data -g www-data -m 750 /var/log/wp-cron

Edite el crontab de ese usuario:

sudo crontab -u www-data -e

Añada una línea:

*/5 * * * * /usr/bin/flock -n /run/lock/wp-cron-example.lock /usr/local/bin/wp --path=/srv/www/example.com cron event run --due-now >> /var/log/wp-cron/example.log 2>&1

Por partes. */5 lo ejecuta cada cinco minutos. flock -n toma un archivo de bloqueo y termina de inmediato si una ejecución anterior todavía lo mantiene. /usr/local/bin/wp es la ruta absoluta que necesita cron. --path permite que el comando se ejecute desde cualquier directorio de trabajo. --due-now procesa sólo los eventos cuya hora ya ha llegado, en lugar de procesar todos los eventos de la cola. La redirección envía la salida normal y los errores a un único archivo que puede leer.

En la práctica, esa redirección no es opcional. Cron envía por correo la salida de una tarea a su usuario, la mayoría de las imágenes de VPS no tienen instalado ningún agente de transferencia de correo y cron registra entonces (CRON) info (No MTA installed, discarding output) y descarta la salida. Un archivo conserva las pruebas.

Compruebe que el archivo se haya guardado:

sudo crontab -u www-data -l

Para varios sitios, use una línea para cada uno y distribuya los minutos para que no se inicien todos a la vez:

*/5 * * * * /usr/bin/flock -n /run/lock/wp-cron-one.lock /usr/local/bin/wp --path=/srv/www/one.example.com cron event run --due-now >> /var/log/wp-cron/one.log 2>&1
2-59/5 * * * * /usr/bin/flock -n /run/lock/wp-cron-two.lock /usr/local/bin/wp --path=/srv/www/two.example.com cron event run --due-now >> /var/log/wp-cron/two.log 2>&1

El registro crecerá indefinidamente si no lo rota. Escriba /etc/logrotate.d/wp-cron:

/var/log/wp-cron/*.log {
    weekly
    rotate 4
    missingok
    notifempty
    compress
    create 640 www-data www-data
}

Compruebe que se analiza sin modificar nada: sudo logrotate --debug /etc/logrotate.d/wp-cron.

Paso 4: confirme que los eventos programados realmente se ejecutaron

Que una línea de crontab se haya guardado correctamente no demuestra nada. Avance desde la comprobación más sencilla hasta la que confirma el resultado.

Primero, ¿cron inició el comando? Cron escribe en el journal mediante su propia unidad:

journalctl -u cron.service --since "15 min ago" | grep wp

Una entrada correcta tiene este aspecto, sin incluir al principio la marca de tiempo ni el nombre del host:

CRON[24913]: (www-data) CMD (/usr/bin/flock -n /run/lock/wp-cron-example.lock /usr/local/bin/wp --path=/srv/www/example.com cron event run --due-now >> /var/log/wp-cron/example.log 2>&1)

Esa línea indica que cron inició el comando como www-data. No indica si el comando se ejecutó correctamente.

Segundo, ¿WordPress ejecutó algo? Lea el archivo de registro:

sudo tail -n 20 /var/log/wp-cron/example.log

WP-CLI escribe una línea por evento y, después, un total:

Executed the cron event 'wp_version_check' in 0.418s.
Success: Executed a total of 2 cron events.

Los errores se escriben en el mismo archivo. Ese es precisamente el objetivo de 2>&1. La mayoría de las ejecuciones no tendrá eventos pendientes y escribirá muy poco. Por eso, lea el archivo después de una ejecución en la que sepa que había trabajo pendiente.

Tercero, confírmelo de principio a fin. Programe un evento marcador y observe cómo desaparece:

sudo -u www-data /usr/local/bin/wp --path=/srv/www/example.com cron event schedule wp_cli_cron_check now
sudo -u www-data /usr/local/bin/wp --path=/srv/www/example.com cron event list --fields=hook,next_run_relative --format=csv | grep wp_cli_cron_check

Espere un intervalo y vuelva a ejecutar el comando de listado. El hook ya no aparece porque un evento único se elimina de la cola cuando se ejecuta. Ningún plugin registra una callback para ese nombre de hook, por lo que ejecutarlo no hace nada más en el sitio. Si el hook sigue apareciendo después de dos intervalos, la cola no se está ejecutando. Las dos primeras comprobaciones le indican si el problema está en cron o en WP-CLI.

No use wp cron test para esto. Ese comando comprueba si funciona la creación de procesos iniciada por un visitante y genera un error cuando DISABLE_WP_CRON es verdadero. En un servidor configurado correctamente, ese error es el resultado esperado, no un fallo.

La alternativa del temporizador de systemd

Si el resto de las tareas programadas del servidor ya se ejecuta como servicios y temporizadores de systemd, incluya también WordPress allí. Cada ejecución aparecerá en systemctl list-timers, y la salida se enviará al journal en lugar de a un archivo que tenga que rotar.

Escriba /etc/systemd/system/wp-cron-example.service:

[Unit]
Description=Run due WordPress cron events for example.com

[Service]
Type=oneshot
User=www-data
Group=www-data
ExecStart=/usr/local/bin/wp --path=/srv/www/example.com cron event run --due-now

A continuación, /etc/systemd/system/wp-cron-example.timer:

[Unit]
Description=Run WordPress cron for example.com every 5 minutes

[Timer]
OnCalendar=*:0/5
AccuracySec=30s
Persistent=true

[Install]
WantedBy=timers.target
sudo systemctl daemon-reload
sudo systemctl enable --now wp-cron-example.timer
systemctl list-timers wp-cron-example.timer
journalctl -u wp-cron-example.service -n 20

systemd no ejecutará dos instancias del mismo servicio al mismo tiempo, por lo que esta versión no necesita flock. Persistent=true hace que recupere una ejecución omitida mientras la máquina estaba apagada, algo que una entrada de crontab no puede hacer.

Elija el crontab o el temporizador. Ejecutar ambos para el mismo sitio significa que la cola se procesa dos veces, y las ejecuciones duplicadas de una tarea de correo electrónico o de pedidos serán visibles para sus clientes.

Por qué la tarea de cron no debe ejecutarse como root

Este es el primero de los tres motivos por los que falla la configuración. Si coloca la tarea en el crontab de root, WP-CLI se detiene antes de hacer nada:

Error: YIKES! It looks like you're running this as root.

La cola nunca se ejecuta y, si no redirigió la salida, nunca verá el mensaje. La solución peligrosa consiste en añadir --allow-root, porque entonces todos los archivos que un plugin escriba durante esa ejecución pertenecerán a root. La siguiente petición web se ejecuta como www-data, no puede escribir en esos directorios y el sitio empieza a mostrar mensajes como este:

Unable to create directory wp-content/uploads/2026/08. Is its parent directory writable by the server?

Corrija el propietario y después mueva la tarea:

sudo chown -R www-data:www-data /srv/www/example.com/wp-content

El crontab de root y el crontab de www-data son archivos independientes, por lo que eliminar la línea de uno no afecta al otro. Compruebe ambos:

sudo crontab -u root -l
sudo crontab -u www-data -l

Por qué cron informa wp: not found

Este es el segundo fallo. Cron proporciona a los trabajos de usuario un PATH muy reducido, /usr/bin:/bin. WP-CLI se instala en /usr/local/bin, que no está incluido en esa lista. El trabajo se inicia, falla en una fracción de segundo y el registro contiene una sola línea:

/bin/sh: 1: wp: not found

Compruebe directamente el entorno de cron en lugar de hacer suposiciones. Añada una línea temporal:

*/5 * * * * env > /tmp/cron-env.txt 2>&1

Lea /tmp/cron-env.txt después de un intervalo y elimine la línea. El valor de PATH= en ese archivo es exactamente el entorno que recibe el trabajo.

Hay dos soluciones. Use la ruta absoluta /usr/local/bin/wp, como en el paso 3. O establezca PATH una sola vez al principio del crontab, antes de todas las líneas de trabajo:

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

El mismo problema aparece un nivel más abajo. El archivo phar wp comienza con #!/usr/bin/env php, por lo que el shell también debe poder encontrar php. Si PHP está fuera de /usr/bin, algo habitual en compilaciones personalizadas y compilaciones de paneles de control, aparece lo siguiente:

/usr/bin/env: 'php': No such file or directory

En ese caso, invoque explícitamente el intérprete, por ejemplo /usr/local/bin/php /usr/local/bin/wp --path=/srv/www/example.com cron event run --due-now.

Por qué un intervalo de un minuto recrea el problema original

Este es el tercer fallo. * * * * * parece más seguro que cinco minutos, pero en un sitio con mucha actividad te devuelve al punto de partida. Si una ejecución tarda más que el intervalo, la siguiente empieza mientras la primera todavía está en curso. Diez minutos después hay diez procesos de PHP, cada uno con su propia memoria y su propia conexión a la base de datos.

Comprueba directamente si se están acumulando procesos:

ps -eo etimes,user,args | grep '[c]ron event run'

etimes es la antigüedad del proceso en segundos. Una sola línea indica un estado normal. Varias líneas con una antigüedad muy superior a tu intervalo indican que las ejecuciones se están acumulando. En un VPS pequeño, esto termina provocando un error de Too many connections de MySQL o haciendo que el kernel termine un proceso de PHP para recuperar memoria. Puedes confirmarlo con sudo dmesg -T | grep -i 'killed process'.

WP-CLI ejecuta directamente las funciones de callback de los eventos en lugar de solicitar wp-cron.php. Por eso, el bloqueo de 60 segundos que WordPress usa para evitar procesos duplicados no se aplica aquí. flock -n en la entrada del paso 3 es lo que impide ahora las ejecuciones simultáneas. Una ejecución omitida termina de inmediato y sin mostrar mensajes, por diseño.

Elige el intervalo a partir de la programación más frecuente de la que realmente dependas y mide primero la duración de una ejecución:

time sudo -u www-data /usr/local/bin/wp --path=/srv/www/example.com cron event run --due-now

Cinco minutos es un valor predeterminado razonable: una publicación programada para las 09:00 se publica antes de las 09:05. Quince minutos es suficiente para un sitio que no tenga ninguna tarea crítica en cuanto al tiempo. Un minuto sólo corresponde a tiendas y plugins controlados por colas que realmente lo necesiten, y únicamente cuando sepas que una ejecución termina en unos pocos segundos.

Si no puede instalar WP-CLI

Algunos hosts bloquean las herramientas de shell. Una solicitud HTTP simple a wp-cron.php ejecuta la misma cola, pero a través de toda la pila web:

*/5 * * * * /usr/bin/curl -sS --max-time 120 "https://example.com/wp-cron.php?doing_wp_cron" > /dev/null

Lo que pierde, de forma clara:

  • La ejecución está limitada por los tiempos de espera del servidor web y de PHP-FPM, por lo que un trabajo largo puede interrumpirse a mitad de camino.
  • El certificado debe ser válido o curl se detiene con SSL certificate problem. Mantenga las renovaciones activas con Certbot en nginx.
  • La caché de páginas no debe almacenar wp-cron.php. De lo contrario, las solicitudes de cron reciben una respuesta almacenada en caché y no se ejecuta nada.
  • No obtiene resultados por evento. La única evidencia de que se ejecutó un trabajo es el efecto que produjo.

-sS mantiene curl en silencio cuando la ejecución es correcta, pero sigue mostrando los errores. Esto es lo adecuado para un trabajo de cron.

Qué más debe incluir la programación del servidor

Una vez que el cron del sistema gestiona la cola de WordPress, mantenga el resto de las tareas rutinarias del servidor en el mismo lugar, donde pueda verlas. Los parches de seguridad del sistema operativo deben gestionarse con actualizaciones desatendidas, no mediante una línea de cron que mantenga manualmente. Las actualizaciones de plugins y temas de WordPress requieren una decisión distinta: wp plugin update --all en un crontab puede dejar fuera de servicio un sitio activo a las 3am sin que nadie lo supervise. Ejecútelas de forma deliberada o después de una fase de staging y una copia de seguridad.

FAQ

¿Desactivar WP-Cron impide que se publiquen las entradas programadas?

No, siempre que otro proceso ejecute la cola. DISABLE_WP_CRON sólo impide que las cargas de página activen la cola. Los eventos siguen programados exactamente igual. Una entrada programada para las 09:00 se publica en la primera ejecución de cron posterior a las 09:00, por lo que un intervalo de cinco minutos la publica como máximo a las 09:05. Si establece la constante y nunca añade la entrada de cron, la entrada permanece en la lista con la marca Missed schedule hasta que algo ejecute la cola.

¿Qué usuario debe ejecutar el trabajo de cron de WordPress?

El usuario propietario de los archivos que escribe PHP, que es www-data en una instalación predeterminada de Ubuntu. Compruébelo con stat -c '%U %G' /srv/www/example.com/wp-content/uploads y compárelo con la línea user = de la configuración del pool de PHP-FPM. Ejecutar el trabajo como root hace que WP-CLI se detenga con un error YIKES, y forzarlo mediante --allow-root deja archivos propiedad de root dentro de wp-content que el servidor web no puede escribir después.

¿Con qué frecuencia debe system cron ejecutar WordPress cron?

Cada cinco minutos es adecuado para la mayoría de los sitios. Ajuste el intervalo al período más corto del que dependa realmente y manténgalo suficientemente por encima del tiempo que tarda una ejecución individual. Puede medirlo anteponiendo time al comando de WP-CLI. En un sitio con mucha carga, los intervalos de un minuto hacen que las ejecuciones se solapen, a menos que flock las controle.

¿Por qué falla wp cron test después de desactivar WP-Cron?

Porque ese comando comprueba la ejecución activada por los visitantes y muestra un error cuando DISABLE_WP_CRON está establecido en true. Ese es el resultado correcto en un servidor configurado de esta forma. Compruebe en su lugar la ruta de system cron: lea /var/log/wp-cron/example.log, o programe un evento marcador con wp cron event schedule y confirme que haya desaparecido de wp cron event list después de la siguiente ejecución.

¿Necesito WP-CLI o basta con usar curl contra wp-cron.php?

curl funciona y es la opción adecuada cuando no puede instalar WP-CLI. Es más lento porque carga WordPress a través del servidor web y está limitado por el tiempo de espera de la petición. WP-CLI ejecuta los eventos en un proceso PHP de línea de comandos sin tiempo de espera web y muestra una línea por evento con su duración. Así, el registro indica exactamente qué se ejecutó y cuánto tardó.

#wordpress#cron#wp-cli#performance#vps