Scheduling backups with WP-CRON (WordPress only)

You can schedule backups to execute automatically using WordPress' WP-CRON task pseudo-scheduler feature. You can find a link to this at the top of every page in Akeeba Backup.


Using WP-CRON in the default configuration as shipped with WordPress is unreliable, per WordPress' own admission, and can result in corrupt backups. When you use a CRON job to trigger wp-cron.php every minute, the backups scheduled with WP-CRON will be reliable but they will take longer than using any other scheduling method. Please read the section on caveats for detailed information.

Kindly note that WP-CRON's design constraints affect equally and to the same degree all backup plugins for WordPress. The other companies refrain from documenting the inherent issues, telling you to use a different scheduling method should you run into problems. We firmly believe in informed consent, therefore we document exactly what the limitations are, and how they may affect backups, so that you can make an informed decision on which backup scheduling method to use.

Click on the New button to create a new backup schedule. Select one or more schedules and click on Delete to remove them.

Each backup schedule has three fields:


This is for your reference, something to remember what this schedule does, e.g. “Weekly full site backup sent to the office NAS”


Choose the backup profile to execute.

CRON Expression

This is a CRON expression which tells Akeeba Backup when and how often to execute the backup. See Supported CRON Expressions.

The time zone used to parse the CRON Expression to determine the next execution time of the scheduled back is the “Backup timezone” you have defined in the plugin's System Configuration page. The default is UTC (Coordinated Universal Time), also called GMT (Greenwich Mean Time) — though the two are not always the same. Keep this in mind when you are scheduling backups. The current timezone and the date and time at the page load time in this timezone are printed on the page, along with a link to the page where you can change the timezone, for your convenience.

Please be advised that if you are using the default WP-CRON configuration shipped with WordPress a scheduled backup MAY NOT run on its next scheduled date and time (or at all), it may take much longer to execute, may not complete, or be inconsistent. Please read about the caveats of how WordPress' WP-CRON works — it also includes solutions for these issues.

Supported CRON Expressions

Scheduling backups in Akeeba Backup requires entering a CRON Expression. The CRON Expression is the standard way to tell servers when and how often to execute scheduled tasks.


You can find examples of CRON expressions and test (most of) your CRON expressions in the third party tool Crontab Guru.

The figure below has a handy cheatsheet with the structure, presets, and special characters of CRON Expressions. Everything is explained in text below.

Figure 4.1. CRON Cheatsheet

CRON Cheatsheet

A CRON Expression can be either a preset or consist of exactly five fields separated by one or more spaces.

All preset names start with the at-sign character. The presets available are:

@yearly or @annually

The backup is scheduled for midnight of January 1st. Equivalent to the CRON Expression 0 0 1 1 *.


The backup is scheduled for midnight of the first day of every month. Equivalent to the CRON Expression 0 0 1 * *.


The backup is scheduled for midnight of every Sunday. Equivalent to the CRON Expression 0 0 * * 0.

@daily or @midnight

The backup is scheduled for midnight of every day. Equivalent to the CRON Expression 0 0 * * *.


The backup is scheduled to run at the top of the hour, every hour, every day. Equivalent to the CRON Expression 0 * * * *.

If you are using a preset you MUST NOT provide any other field or text in the CRON Expression edit box.

If you do not wish to use a preset you will have to provide five fields, separated by one more spaces from each other. The fields are always five in number and appear in this order: minute, hour, day of the month, month, weekday. The hour is expressed in 24-hour format (if you're from the US or Canada you may be calling that “military time”). The day of the month, month, and weekday follow the Gregorian calendar which is the calendar used by the vast majority of Western countries.

The acceptable ranges for each field are:




0–23. This is using the 24-hour clock where 0 is 12am (midnight), 12 is 12pm (noon), 13 is 1pm and so on.

Day of month

1–31. Please note that using the value 31 does nothing for the months of February, April, June, September, and November which have 28 to 30 days. Using 30 does nothing on February for the same reason. Using 29 will only work on February of leap years. If you want the last day of the month use the letter L which stands for “Last”. See further below about special characters. Further information can be found in this ServerFault discussion.


1–12 with 1 being January. You can use the string literals JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC if you want. The string literals can be uppercase (e.g. JAN), lowercase (e.g. jan), or mixed case (e.g. Jan, JAn, jAN, jaN).


0–7 with 0 = Sunday, 1 = Monday, …, 6 = Saturday, 7 = Sunday. Yes, Sunday can be either 0 or 7, both work fine.

You can alternatively use the string literals SUN, MON, TUE, WED, THU, FRI, SAT if you want. The string literals can be uppercase (e.g. SUN), lowercase (e.g. sun), or mixed case (e.g. Sun, SUn, sUN, suN).

The fields of CRON expressions accept some special characters as well:

* (Every)

Using a star character means "every". If you put this in the MInutes field the expression matches every minute of the hour. It can be combined with / (Divider) to create frequencies. For example, */5 means "every fifth".

/ (Divider)

Using a forward slash followed by a number allows you to define a frequency. For example, */3 means "every third". There must be no whitespace between the divisor, the forward slash, and the divider.

The divider must be a number that the field can be wholly divided by. For minutes the allowed dividers are 2, 3, 4, 5, 6, 10, 12, 15, 20, 30 (the whole dividers of 60, the number of minutes in an hour). For hours this is 2, 3, 4, 6, 8, and 12 (the whole dividers of 24, the number of hours in a day). It's not recommended to use this for the day of the month or the weekday; the former's range depends on the month and whether it's a leap year (it can be 28, 29, 30, or 31) whereas the latter is a prime number, therefore indivisible by any integer to produce a whole result. Using dividers in these cases may result in unpredictable results.

, (List)

Using one or more commas you can give a list of discrete values. For example 1,4,6,8 means "only on the 1st, 4th, 6th, and 8th". There must be no whitespace between the values and the comma.

- (Range)

Using a dash you can give an inclusive range. For example 1-6 is equivalent to 1,2,3,4,5,6. There must be no whitespace between the values and the dash.

L (Last)

Only valid in the day of month and weekday fields.

In the day of month it can be present bare or part of a list, e.g. L (only the last day of the month) or 1,15,L (first, 15th, and last day of the month).

In the weekday field it is always preceded by a number indicating the date of the week. It CANNOT be combined with any other special character. For example 1L means “the last Monday of the month”.

W (nearest working Weekday)

Only valid in the day of month field. It CANNOT be combined with any other special character. For example, 15W means the nearest non-weekend day of the week around the 15th day of the month.

This modifier remains strictly within the boundaries of the month. For example if you use 1W (first non-weekend day of the month) and the 1st of the month is a Saturday, this will match the 3rd day of the month which is a Monday. It will not go back to the previous month. Likewise, if you use 31W for the first non-weekend day of the month around the 31st of the month but the 31st of the month is a Sunday this will result in Friday the 29th being used, not the first day of the next month.

# (n-th weekday of the month)

Only valid in the weekday field. It is given as day of the week, hash sign, ordinal. There must be no whitespace between the day of the week, hash sign, and ordinal.

For example 5#2 means the 2nd Thursday (5 = Thursday) of the month.

According to the test cases of the third party library we're using for parsing CRON expressions, the day of the week must be provided as a number, not as a string literal. This means that something like THU#5 shouldn't work.

Practical examples
0 3 * * *

Every day at 3:00 am

0 6 * * 7#2

The second Sunday of every month at 6 am

0 * * * MON-FRI

Every hour, on the hour, as long as it's a weekday (Monday to Friday)

What you cannot do with CRON expressions

You cannot have date expressions based on whole weeks. For example there is no way to specify "every second Sunday" or "every third Monday".

Why use CRON expressions?

CRON has been around to schedule tasks in computer systems since the 1970s. Trying to invent something better than a battle tested solution of several decades is not just hubris, it's completely pointless. Yes, we tried it anyway, and predictably failed for the reason succinctly demonstrated in the relevant XKCD comic.

There were several ideas about how the scheduling drafted, tested, and thrown away between 2017 when implementing this feature found its way into our to-do list and 2023 when it was finally implemented. Every one of them had a major issue. The least problematic of them all is, indeed, CRON expressions. Apparently there's a reason they are still in use since the 1970s: they work, they work well, and it's the devil we know (there's a very large number of IT professionals who understand the limitations of CRON expressions and won't fall for them).

We think that the few edge cases not covered by CRON Expressions are a small price to pay over having to develop, test, and train users to use a completely different scheduling system nobody has ever tried before.

In an attempt to preempt what is going to be a frequently asked question, let us tell you why we didn't go with the "obvious" approach of "run this task ever X amount of time" where X is a static quantity. After all, that's what WP-CRON is doing under the hood, right?

It has to do with the the Three Horsemen of The Task Scheduling Apocalypse: Daylight Savings Time (DST), Leap Seconds, and Inadvertent Overlap.

Let's say you want to execute something every 1209600 seconds (that's 2 weeks) starting February 28th at exactly midnight (00:00:00). This task would execute at 1am starting sometime in March, when the DST comes into effect, and go back to executing at Midnight sometime in October when the DST is no longer in effect. To make things even more interesting, the exact dates this will happen depend on the timezone, even for countries which are normally the same amount of time ahead or behind GMT when DST is not in effect.

If a leap second is added at the end of the year, the backup on January 1st would start at 00:00:01 instead of 00:00:00. This is not all that important on the face of it, until you realise that in the best case scenario WP-CRON executes once every 60 seconds, at the top of the minute… or thereabout. This means that some days WP-CRON would execute at exactly 00:00:00 and would consider the backup scheduled to start at 00:00:01 to be "in the future", therefore the backup would start at best at 00:01:00 — one minute late. Some days WP-CRON would execute a couple of seconds after midnight (e.g. because the server load was a bit higher that day, perhaps because the host was installing updates) in which case 00:00:01 is in the past and the backup starts at 00:00:01 or thereabout. In both cases you'd have a server side CRON job executing WP-CRON every single minute, making this discrepancy a completely maddening occurrence. So, yes, even a second off can be important after all.

Finally, as we'll be mentioning further below, WP-CRON is an unreliable scheduler in its default configuration. Since it doesn't run continuously and it has to carry out other tasks except ours it may not execute our backup task for a while. What if it hasn't executed our task for the past 1209598 seconds since its scheduled backup time? Should we start the task or not? If we do we'll definitely end up with two backups running, having started two seconds apart. We "obviously" should not do that. But what is not actually obvious is how long after the scheduled task start we should give up trying to start the task. What we should do for a backup that runs once every two weeks, once a week, once a day, and once an hour is different. What we should do for a backup that runs once a day and takes 10 minutes to another one that also runs once a day but takes 10 hours are very different as well. In the end of the day, how long is a piece of string?

So, no, the "obvious" solution is far from being either obvious or a solution when you have to do with non-trivial, critical tasks like backups.