Open-source project
nextcloud/previewgenerator avatar
nextcloud/previewgenerator

Preview Generator trades disk for latency, and the trade still matters on cheap hardware

Nextcloud app to do preview generation in the background.

523 stars56 forksPHPAGPL-3.0

At a glance

What is it?
The Nextcloud Preview Generator app listens for file writes, records them, and produces thumbnails in a background job so the first person to open a file does not pay for the conversion. It does not replace on-demand generation, and it does not work with encryption enabled.
Who is it for?
Adopt Preview Generator if you run a small Nextcloud instance on modest hardware where on-demand thumbnail conversion is too slow to be usable, and you have the disk to spend. Do not adopt it on a current large instance, because the README says the concurrency limit that motivated it now exists in Nextcloud itself, and do not enable it on an encrypted instance, where the app does not work at all.
Can I use it commercially?
Yes, with strict conditions. AGPL-3.0 is a network copyleft licence: if people use a modified version over a network, for example as a hosted service, you must offer them its source code under the same licence.
Is it still maintained?
Yes. The repository last received commits 11 days ago.
What is it written in?
Mainly PHP, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The problem: first view of a file pays for the conversion

Preview Generator is a Nextcloud app that allows admins to pre-generate previews. The app listens to edit events and stores this information, and once a cron job is triggered it starts preview generation.

The reason to do that is stated plainly: you can better utilize your system by pre-generating previews when your system is normally idle, and thus put less load on your machine when the requests are actually served.

That is a scheduling problem rather than a rendering problem. Nextcloud already knows how to generate a thumbnail. What it did not do, in the version this app was written for, was do it ahead of time, so the cost landed on whoever happened to open the folder first. Moving that cost to an idle window is the entire value proposition.

The app is primarily meant for small Nextcloud servers running on cheap hardware where on-demand generation of previews is not quick enough. And the README is direct about the cost of the approach: the app effectively trades higher disk usage for quicker previews, and this trade-off should be considered carefully.

That sentence is the most useful line in the documentation, because it tells you the app is not free. Every preview size you enable is another set of image files multiplied across every file in the instance.

The primary language is PHP, the licence is AGPL-3.0-or-later, and the app is published in the Nextcloud app store. The last push to the repository was on 2026-10-12.

An event queue, and why the job stops after five minutes

How the app works is three steps, and each one is a design decision you can see the consequences of.

First, it listens to events that a file has been written or modified and stores it in the database. Second, it generates previews for the files that have been written or modified in a background job. Third, optionally, a dedicated occ command generates previews using a custom schedule, for example in a separate system cron job.

The consequence of the first step is worth stating, because it is the difference between this and a bulk generator. The queue is event driven, so the app knows what changed rather than rescanning the whole filesystem. The `preview:pre-generate` command only processes new or modified files, meaning since the app was enabled or the last pre-generation was done.

The background job has a hard limit. By default it is limited to a maximum execution time of five minutes, and additionally it requires using the cron background job mode, because webcron and AJAX modes are not supported. The background job is limited to prevent stalling the PHP process.

That limit is the interesting part of the design. A Nextcloud background job runs inside a PHP process, and a preview conversion of a large video can take long enough to look like a hang. Capping the run at five minutes and then resuming later turns an unbounded job into a bounded one, at the cost of the queue draining more slowly.

The limits are configurable through app configs, and the limits are on execution time and on preview count. The README also points at a diagnostic command, `preview:queue-stats`, which you can run to check the performance of your configured preview limits. If the queue stats show a large number of stale previews, the guidance is to increase, or remove, the configured limits of the background job.

The three occ commands and what each one is for

The app exposes three commands, and they are not interchangeable.

`preview:generate-all` loops over all files and tries to generate previews for them. It accepts a user list and path filters.

bash
preview:generate-all [--workers=WORKERS] [--path=PATH ...] [user_id ...]

If one or multiple user ids are supplied it will just loop over the files of those users, and you can limit the generation to one or more paths using `--path="/[username]/files/[folder path]"`, for example `--path="/alice/files/Photos"`. Note that all given user_ids are ignored if at least one path is specified.

That last note is a small trap. The two filters cannot be combined, and the path silently wins.

The command also takes `--workers` to spawn multiple parallel workers, for example `--workers=10`, which is how you speed up the initial run.

`preview:pre-generate` does the actual pre-generation for new or modified files, and it is possible to run the command multiple times in parallel to speed things up.

bash
preview:pre-generate

The third is the diagnostic.

bash
preview:queue-stats

Both of the generating commands accept `-vv` for verbose output if you want to see which files are being processed, which is the difference between a run that appears stuck and a run you can watch.

The install steps say to run `./occ preview:generate-all` once after installation, and then to add a system cron job for `./occ preview:pre-generate`, with the author running it every ten minutes.

Preview sizes are a power of four, and every one costs disk

The configuration section is where the disk cost becomes concrete, and it has one rule that is easy to get wrong.

The value of each option can either be a list of sizes separated by spaces or an empty string. Setting an empty string will simply skip those kinds of previews. Deleting or not setting a config uses a built-in default list of values.

Then the constraints: preview sizes must be a power of 4, other sizes are silently ignored. The smallest possible size is 64. The max size is determined by your `preview_max_x` and `preview_max_y` settings in `config.php`.

Silently ignored is the operative phrase. A configuration with 100 in it does not fail loudly, it produces no previews for that size, and the admin sees a missing thumbnail rather than a config error.

The five size categories map to how the file app displays images.

bash
occ config:app:set --value="64 256" previewgenerator squareSizes
occ config:app:set --value="256 4096" previewgenerator fillWidthHeightSizes
occ config:app:set --value="64 256 1024" previewgenerator widthSizes

Square previews are cropped squares used mostly in the list and tile views of the files app. Fill keeps the aspect ratio and uses the given size for the longer edge. Cover keeps the aspect ratio and uses the given size for the shorter edge. Width keeps the aspect ratio and uses the specified width, with the height scaled to the aspect ratio. Height is the mirror image.

So the trade is explicit: each enabled size is another full set of thumbnails across the whole instance, and the square and fill categories are for list views while width and height are for single-file views.

Two limits on the background job, and a warning about configuring both

The background job has two separate limits and the interaction between them is specified, which is unusual and useful.

The execution time limit is set in seconds.

bash
occ config:app:set --value=300 --type=integer previewgenerator job_max_execution_time

A value of zero means unlimited. The default is the five minutes described earlier.

The preview count limit works the same way.

bash
occ config:app:set --value=0 --type=integer previewgenerator job_max_previews

A value of zero means unlimited here too, and the configuration guidance is to configure one, both or no limit, with no limit explicitly not recommended. In case both limits are configured, the more restrictive one takes precedence.

That last rule is the one to remember. If you set execution time to 300 seconds and preview count to 1000, the job stops at whichever it hits first, and you cannot tell from the configuration which one ended the run without checking the logs.

There is also a switch to turn the built-in background job off.

bash
occ config:app:set --value=false --type=boolean previewgenerator job_disabled

The recommendation is to disable the default background job in case a custom system cron entry with `occ preview:pre-generate` is configured. In other words, if you are running your own cron entry every ten minutes as the usage steps describe, leaving the internal job enabled means two things are draining the same queue at once, with the internal one limited to five minutes and yours unlimited.

The README does not describe what happens when both are running, so the safe configuration is the one it suggests: use your own cron and disable the internal job.

What Preview Generator does not do, and when it is the wrong app

Two limits are stated in the documentation and one of them is a hard stop.

The first is that the app does not replace on-demand preview generation, so if a preview is requested before it is pre-generated it will still be shown. This is a design decision rather than a limitation, and it is the correct one: pre-generating is an optimisation, not a gate. Your users never see a missing image because the background job has not caught up.

The second is that the app does not work with encryption enabled. That is the hard stop, and it is one line in the known issues section. If your instance has server-side encryption turned on, this app is not for you, and no amount of configuration changes that.

The third consideration is the one the README talks you out of the app. The previous reasoning of resource exhaustion caused by too many concurrent preview requests is not a concern anymore, as there is a configurable limit in modern versions of Nextcloud.

So the original motivation has been partially absorbed into the server. If you are on a current Nextcloud with a sensible preview concurrency limit, the case for pre-generating is weaker, because the pathological case that produced the exhaustion no longer occurs.

What remains is the cheap-hardware case. On a small instance with a slow disk and a modest CPU, the first preview of a folder can still take long enough to feel broken, and moving that work to an idle window is still the right answer. That is a narrower case than it was, but it is a real one.

One more thing the README does not cover is how the queue behaves when the instance is heavily loaded, or what happens to the recorded events if the app is disabled and re-enabled. The `preview:queue-stats` command is the tool for the first question, and the second is a matter of reading the code in lib/.

Editorial conclusion

Adopt Preview Generator if you run a small Nextcloud instance on modest hardware where on-demand thumbnail conversion is too slow to be usable, and you have the disk to spend. Do not adopt it on a current large instance, because the README says the concurrency limit that motivated it now exists in Nextcloud itself, and do not enable it on an encrypted instance, where the app does not work at all. Verify first how many preview sizes you actually need, since each configured size is another copy of every thumbnail on disk.

Frequently asked questions

What does the Nextcloud Preview Generator app do?

It listens to edit events for files being written or modified, stores that information in the database, and then generates previews for those files in a background job. The goal is to put the preview work in an idle window so that serving a request is cheap.

How do I install and use Preview Generator?

Install it from the Nextcloud app store or clone the repository into your Nextcloud app folder, enable it, run ./occ preview:generate-all once after installation, and then add a system cron job for ./occ preview:pre-generate, for example every ten minutes.

Why can I not use webcron with Preview Generator?

The background job requires the cron background job mode, and webcron and AJAX modes are not supported. The job is also limited to a maximum execution time of five minutes by default, to prevent stalling the PHP process, and that limit is configurable.

What sizes can I configure for previews in Nextcloud Preview Generator?

Preview sizes must be a power of 4, other sizes are silently ignored, and the smallest possible size is 64. The maximum is determined by the preview_max_x and preview_max_y settings in config.php, and each enabled size multiplies the disk used for thumbnails.

Does Preview Generator work with Nextcloud encryption?

No. The known issues section states that the app does not work with encryption enabled, so it is not an option on an instance with server-side encryption turned on.

Official sources

  1. Issues
  2. License: AGPL-3.0
  3. nextcloud/previewgenerator on GitHub
  4. Project website
  5. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/nextcloud-previewgenerator.svg)](https://hysenlabs.com/projects/nextcloud-previewgenerator)

Was this useful?