To make Nextcloud background jobs run automatically with systemd, configure a one-shot service that runs cron.php as the web-server user, then enable a timer that starts that service every five minutes. The key checks are: the timer is enabled and has a next run time, the service points to the correct Nextcloud and PHP paths, and the service journal shows a successful exit. A one-shot service may show “inactive (dead)” between runs; that can be normal after it finishes.
This setup is for a conventional Nextcloud installation on Ubuntu using systemd, with Nextcloud files available on the host. If Nextcloud runs inside Docker, Snap, or an appliance such as Nextcloud All-in-One, use that distribution’s documented scheduler method instead of pointing a host unit at a container-only path. Examples below use /var/www/nextcloud and www-data; replace them with the real install path and HTTP user on your server.
What the timer should accomplish
Nextcloud’s cron.php runs queued background work such as maintenance and app tasks. The systemd timer is the clock; the service is the command that runs when the timer elapses. You need both. Creating the files without enabling the timer leaves the schedule inactive, while enabling a timer that points to the wrong PHP or Nextcloud path can produce repeated failed runs.
Nextcloud recommends system cron for production background jobs. Its current administration manual also documents systemd timers as an alternative. The example schedule starts five minutes after boot and then schedules another run five minutes after each service activation. It avoids relying on a user visiting the web interface.
1. Confirm the installation path, user, and PHP CLI
Before editing units, locate the actual Nextcloud directory and check which PHP command the web server installation expects. On a typical Ubuntu archive installation, the path may be /var/www/nextcloud, the web user is often www-data, and PHP CLI is /usr/bin/php. Verify instead of assuming:
ls -l /var/www/nextcloud/cron.php
command -v php
php -v
If your Nextcloud directory is elsewhere, substitute that directory in both unit commands below. If the web server uses a separate PHP version, use that version’s CLI executable in ExecStart and ExecCondition. A systemd service does not inherit the interactive shell’s PATH or environment, so explicit paths are more reliable.
Test the status check under the same user systemd will use:
sudo -u www-data /usr/bin/php -f /var/www/nextcloud/occ status -e
Nextcloud’s occ command should run as the HTTP user to preserve correct file ownership and permissions. If this test reports a PHP error, maintenance mode, or a missing file, fix that first; the timer cannot compensate for a failing Nextcloud command.
2. Create the one-shot service
Create /etc/systemd/system/nextcloudcron.service:
sudo nano /etc/systemd/system/nextcloudcron.service
Enter this unit, adjusting the path and user if your installation differs:
[Unit]
Description=Nextcloud cron.php job
[Service]
User=www-data
ExecCondition=/usr/bin/php -f /var/www/nextcloud/occ status -e
ExecStart=/usr/bin/php -f /var/www/nextcloud/cron.php
KillMode=process
ExecCondition checks whether Nextcloud is operating normally before launching the background job. If the condition fails, systemd skips the run; inspect the journal to learn why. KillMode=process allows external programs started by a background job to continue after the main cron process exits. The current Nextcloud example does not require an [Install] section in this service file.
Using /usr/bin/php is appropriate only when that is the correct CLI interpreter on your server. For example, a custom PHP repository may install a versioned binary. Use the same compatible PHP version that Nextcloud requires and that its web server configuration uses.
3. Create the timer
Create /etc/systemd/system/nextcloudcron.timer:
sudo nano /etc/systemd/system/nextcloudcron.timer
Add:
[Unit]
Description=Run Nextcloud cron.php every 5 minutes
[Timer]
OnBootSec=5 min
OnUnitActiveSec=5 min
Unit=nextcloudcron.service
[Install]
WantedBy=timers.target
OnBootSec schedules the first run five minutes after systemd starts at boot. OnUnitActiveSec schedules another run five minutes after the service was last activated. The timer is not the same as the service: enable and start the .timer unit to make the schedule run automatically.
4. Reload systemd and enable the timer
Ask systemd to read the new unit files, then enable and start the timer in one command:
sudo systemctl daemon-reload
sudo systemctl enable --now nextcloudcron.timer
Check the timer:
systemctl status nextcloudcron.timer
systemctl list-timers --all | grep nextcloudcron
The timer should be loaded and active, and list-timers should show a future time under NEXT. If it is disabled, not loaded, or has no next activation, confirm that the filename ends in .timer, the [Install] section contains WantedBy=timers.target, and you ran daemon-reload after saving the files.
5. Run one test and inspect its result
You can launch the service once without waiting for the next timer tick:
sudo systemctl start nextcloudcron.service
sudo systemctl status nextcloudcron.service
sudo journalctl -u nextcloudcron.service -n 50 --no-pager
Look for the service’s result and any PHP or Nextcloud errors. After the command finishes, nextcloudcron.service may return to inactive (dead) because it is a one-shot task. That alone does not mean it failed. The timer should remain active and schedule the next run; the journal should show whether the previous run exited successfully.
In the Nextcloud Administration settings, open the background job status area and confirm that the last job execution time advances after the timer fires. The overview warning should clear when Nextcloud detects recent background activity. Allow at least one interval after enabling the timer before deciding that the schedule has not run.
Diagnose the common failure patterns
| What you see | What to check | Next step |
| The timer is “inactive” or disabled | Whether the timer unit was enabled and started | Run sudo systemctl enable --now nextcloudcron.timer, then check systemctl list-timers |
| The timer is active, but the service fails | The service journal, PHP executable, Nextcloud path, and service user | Run the status test as www-data; fix the first reported PHP or file-permission error |
| The service is inactive between runs | The timer’s NEXT time and the service’s last journal entry | If the service exited successfully and the timer remains active, this is normal for a one-shot unit |
The service exits without running cron.php | The ExecCondition result and whether Nextcloud is in maintenance mode | Resolve the condition failure and check Nextcloud status before manually starting the job again |
| The Admin page still reports that cron has not run | Whether the service really invokes the correct instance and whether its last execution time changes | Wait through a timer interval, inspect logs, and verify you are checking the same Nextcloud installation |
For more detail, review recent service logs and the Nextcloud log file:
sudo journalctl -u nextcloudcron.service --since "30 minutes ago" --no-pager
sudo -u www-data /usr/bin/php -f /var/www/nextcloud/occ background-job:list
The job list shows registered work, but it is not by itself proof that the timer is executing. Use the timer’s last and next activation times, the service journal, and Nextcloud’s last-run indicator together.
When to use a different scheduler
Systemd timers work well when the server runs systemd and you want a local, service-managed schedule. If you already operate a standard cron daemon and can verify its crontab, a system cron entry that calls the same cron.php as www-data is also supported. Do not schedule both methods for the same Nextcloud instance; overlapping invocations can waste resources and complicate troubleshooting.
AJAX scheduling depends on users visiting Nextcloud and is less reliable for a busy or multi-user server. Webcron calls cron.php over HTTP and may be suitable for a very small instance where system access is unavailable, but Nextcloud notes that web execution limits how much work can run per call. If Nextcloud is containerized or managed by an appliance, use its supported scheduler or container configuration; a host service using /var/www/nextcloud will not work if that path is not present on the host.
A healthy setup has an enabled timer with a recurring NEXT time, service runs that complete without PHP errors, and a Nextcloud last-run time that advances. If all three are true but a particular app task still appears delayed, the scheduler may be working while that job waits for its own schedule, maintenance window, or queue conditions. Diagnose that job separately before changing the timer interval.
Official references