---
title: "Magento 2 cron and LiteMage"
date: "2018-08-01"
path: "/188-magento-2-cron-and-litemage.html"
section: "/ramblings"
description: "Setting up the Magento 2 cron jobs as the right user and installing the LiteMage full-page cache module on a LiteSpeed server."
---

Two jobs that come up on every Magento 2 build I do: getting cron running properly, and putting a full-page cache in front of the store. On servers running LiteSpeed rather than Apache, the cache is LiteMage. This was Magento 2.2 in mid-2018.

## Why cron matters on Magento 2

Magento 1 would limp along without cron. Magento 2 won't. Cron drives the indexers when they're set to "Update by Schedule", sends order and transactional emails from the queue, applies catalogue price rules, cleans up the cache, and generates sitemaps. A store without cron looks fine for a day or two and then prices are wrong, emails haven't gone out and the admin shows a "one or more indexers are invalid" banner.

## The crontab

Cron has to run as the user that owns the Magento files. On a Virtualmin server that's the domain's user, not root and not `www-data`. If you run it as root, files it writes under `var/` and `generated/` end up owned by root and the site starts throwing permission errors.

```bash
crontab -u example -e
```

For Magento 2.1 and 2.2 there are three entries:

```text
* * * * * /usr/bin/php /home/example/public_html/bin/magento cron:run | grep -v "Ran jobs by schedule" >> /home/example/public_html/var/log/magento.cron.log
* * * * * /usr/bin/php /home/example/public_html/update/cron.php >> /home/example/public_html/var/log/update.cron.log
* * * * * /usr/bin/php /home/example/public_html/bin/magento setup:cron:run >> /home/example/public_html/var/log/setup.cron.log
```

- `cron:run` is the main one. It reads the schedule from every module's `crontab.xml` and runs whatever is due. The `grep -v` drops the "Ran jobs by schedule" line it prints on every run, so the log only shows something when there's something to see.
- `update/cron.php` and `setup:cron:run` serve the Web Setup Wizard and Component Manager in the admin. If you only ever upgrade through Composer on the command line, they do nothing useful, but the wizard's readiness check fails without them.

Use the same PHP binary the site uses. On a server with several PHP versions, `/usr/bin/php` may not be it; give the full path to the right one, for example `/usr/bin/php7.1`.

Magento 2.2 can write the entries for you:

```bash
cd /home/example/public_html
sudo -u example bin/magento cron:install
crontab -u example -l
```

It wraps its lines in `#~ MAGENTO START` and `#~ MAGENTO END` markers so it can find and replace them later. Leave the markers alone.

## Checking it runs

Wait a couple of minutes and look at the schedule table:

```sql
SELECT job_code, status, scheduled_at, executed_at, finished_at
FROM cron_schedule
ORDER BY scheduled_at DESC
LIMIT 20;
```

You want a steady stream of `success` and `pending` rows. Lots of `missed` means cron isn't running every minute; rows stuck at `running` usually mean a job died part way, often on memory. The PHP CLI has its own `php.ini`, so check `memory_limit` there as well as in the web server's PHP.

## LiteMage

LiteMage is LiteSpeed's full-page cache for Magento. It does the same job as Varnish but runs inside the LiteSpeed web server, and it uses edge side includes for the blocks that change per visitor (basket, account links), so pages can be cached for everyone and the personal parts filled in separately. It needs LiteSpeed Web Server Enterprise with a LiteMage licence; on plain Apache or OpenLiteSpeed there's nothing for the module to talk to.

Install the module with Composer, as the file owner, from the Magento root:

```bash
composer require litespeed/module-litemage
php bin/magento module:enable Litespeed_Litemage
php bin/magento setup:upgrade
```

`setup:upgrade` drops the store out of production mode's compiled state, so rebuild it:

```bash
php bin/magento setup:di:compile
php bin/magento setup:static-content:deploy en_GB en_US
php bin/magento deploy:mode:set production --skip-compilation
php bin/magento indexer:reindex
php bin/magento cache:flush
```

List every locale the store uses in `setup:static-content:deploy`. Without them it only builds `en_US`, and a store view set to British English falls back to generating files on demand, which is slow and sometimes broken. `setup:di:compile` takes no locale arguments.

Switch LiteSpeed on for the site in the `.htaccess` in the Magento root:

```apache
<IfModule Litespeed>
    LiteMage on
</IfModule>
```

Then in the admin go to Stores, Configuration, Advanced, System, Full Page Cache and set Caching Application to LiteMage. Flush the cache once more.

## Checking LiteMage

Request a category page twice and look at the headers:

```bash
curl -sI https://www.example.com/some-category.html | grep -i x-litespeed-cache
```

A `hit` on the second request means it's working. If you never get a hit, the usual causes are the licence not including LiteMage, the `.htaccess` lines missing, or something in the theme setting a cookie on every request.

The two halves are connected: when indexers run on schedule, it's cron that tells LiteMage which pages to purge after a price or stock change. If cron stops, the cache keeps serving the old prices.
