Nextcloud Face Recognition, grouping faces in your own photo library
Nextcloud app that implement a basic facial recognition system.
At a glance
- What is it?
- Face Recognition is a Nextcloud app that detects and groups faces in the photos already on your server, using dlib through PHP bindings, and it is built so the images never leave the instance. The analysis is queued and run as a background job, and users have to opt in before their photos are analysed.
- Who is it for?
- Face Recognition suits a Nextcloud operator who wants photo face grouping without uploading a private library to a cloud service, and who can afford to run a scheduled analysis job on a server with a gigabyte of memory to spare. It does not suit an instance that cannot install PHP extensions, since dlib bindings are required, and it does not suit a large library analysed on a shared host.
- 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 received new commits within the last day.
- 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 24, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Photos that never leave the server
Face Recognition is a Nextcloud application with the goal of recognizing, analyzing and aggregating face data in users' images, and providing additional functionality on top of that information, all with the built-in privacy of Nextcloud.
The comparison the project makes is worth repeating because it states the scope precisely: imagine Google Photos, but only for faces, not detecting objects, and in such a way that your images never leave your Nextcloud instance.
That is a narrow, well-drawn scope. It is not a photo management application that happens to have a face feature, and it is not a face recognition service with a Nextcloud front end. It is one capability, added to a storage platform that already holds the images, with the privacy property that the analysis happens on infrastructure you control.
The privacy claim has a specific design consequence that shows up in the workflow. The application listens for the creation of new image files and queues them for later analysis. A scheduled task, or an administrator on demand, takes that queue and analyses the images for faces, identifying them if possible and grouping them by similarity.
So the analysis is not synchronous with the upload. An image is uploaded, it is noticed, it is queued, and some later job does the work. That matters for three reasons. The upload path stays fast, because no face detection is on it. The CPU cost is concentrated in a job you can schedule, pause and run at a time when the server is otherwise idle. And the queue is a natural place to enforce the consent model, because an image that has not been approved for analysis simply never enters it.
The licence is AGPL-3.0, and the package manifest states it as AGPL-3.0-or-later. The primary language is PHP, and the topics name dlib, face-detection, face-recognition, hacktoberfest, nextcloud, pdlib and photos.
The requirement list is short and the dlib dependency is the hard part
The requirements are stated as a short list, and one of them dominates the others.
Nextcloud 34 is the platform version. The Dlib PHP bindings are required, with a link to the goodspb/pdlib project, which is the PHP extension that exposes dlib to PHP. PHP Bzip2 is required. And 1GB of RAM is the memory requirement.
The dlib bindings are the hard part because they are a native extension. dlib itself is a C++ library, and it has to be compiled against your PHP version, and the compile has to match the PHP build you are running. There is no way to install this app on a shared hosting plan where you cannot load your own extensions, and there is a real chance the pre-built binaries do not match your distribution's PHP build, in which case you are compiling it yourself.
The RAM figure is worth taking seriously rather than treating as a minimum. 1GB is the stated requirement for the app as a whole, and the configuration section separately asks you to indicate how much memory to assign to image processing. That setting has a direct impact on memory consumption, and the temporary file size setting is described as depending on your installation. So there are two knobs, and both of them are about how much memory the analysis of one image will take.
The reason memory is the constraint at all is the second requirement, PHP Bzip2. The face models shipped for this kind of tool are compressed archives, and the extension is what reads them. A model is a set of trained feature descriptors, and loading one and computing descriptors for every face in a photograph is a memory-heavy operation, which is why the app asks you to cap it and why the background job takes a time limit.
The installation path is the ordinary one for a Nextcloud app. If the requirements are met, you can install and enable it from the Nextcloud app store, and the wiki holds the installation documentation. For development or testing, you build from source.
cd /path/to/nextcloud/apps/
git clone https://github.com/matiasdelellis/facerecognition.git
cd facerecognition/
makeThe Makefile builds more than the JavaScript
The `make` command does a five-step build, and the documentation is emphatic about a detail that trips people up.
The Makefile will install the PHP dependencies with composer, install the JavaScript dependencies with npm, build the Vue.js frontend components with webpack, copy vendor JavaScript libraries, meaning lozad, handlebars, autocomplete and egg.js, and compile the Handlebars templates.
And then the warning: running only `npm run build` is not sufficient. You must run the full `make` command, or `make build`, to ensure all dependencies are properly built and copied to the `js/` directory.
That warning exists because the JavaScript build is not self-contained. The vendor libraries are copied out of `node_modules` into a `js/vendor/` directory by the Makefile, and the Handlebars templates are compiled separately from the webpack step. The Makefile encodes that as file targets with dependencies, so a change to `node_modules` triggers the copy, and the `js/vendor/handlebars.js` and `js/vendor/lozad.js` targets each depend on the npm dependency step.
Two of the vendor files are not copied at all but downloaded, which is a detail worth knowing for a reproducible build. The autocomplete script is fetched with wget from a raw GitHub URL on a specific repository, and the egg.js library likewise. So a source build reaches out to the network for those two files, and their contents are whatever is on that branch of that repository at build time. That is a supply-chain consideration for an app that is going to be installed on a server holding someone's photo library, and it is a fair thing to check before running the build on a machine you care about.
The Makefile also handles the case where composer is not installed, by downloading the installer and running the phar directly from a tools directory in the build tree, and it runs both `composer install --prefer-dist` and `composer update --prefer-dist`, which is an unusual pairing.
For day-to-day front-end work there is a watch mode, and the documentation notes the follow-up step you may need after changing things.
npm run watch # Watches and rebuilds webpack bundlesAfter making front-end changes you may need to run `make vendor-deps` and `make js-templates` if you modified vendor dependencies or the Handlebars templates. The package manifest requires Node 24 or newer and npm 9 or newer, and the build scripts are the standard webpack ones with a production and a development node environment.
Configuration, models and the command that does the work
Before any image is analysed, three things have to be set up, and one of them is a command you have to know about.
First, you indicate how much memory to assign to image processing. Second, you must properly install and configure the pretrained models using the `occ face:setup` command, with the wiki page on models linked for the details. Third, you indicate the size of the images used in the temporary files, from the Nextcloud settings panel, and the documentation is clear that this configuration depends on your installation and has a direct impact on memory consumption.
The `occ face:setup` command is the one that catches people. The models are not shipped with the app in a usable form, they have to be fetched and installed through the setup command, and until that has run the analysis cannot work. It is the equivalent of a first-run wizard for a headless application.
The command that does the actual work is `occ face:background_job`, and the documentation recommends testing the application intensively before pointing it at real user data. The suggested method is to create a new user in your Nextcloud instance, upload some photos from the internet, then run the background job for that user and evaluate the result.
The command is `occ face:background_job -u new_user -t 900`.
The `-u` flag names the user whose queue is processed, and `-t 900` is the time limit in seconds, so fifteen minutes. The time limit is what makes this safe to run on a production instance: the job processes the queue until the budget is exhausted, so a huge library is analysed over a series of runs rather than in one long job that competes with real traffic.
There is a requirements check that runs at the start of `occ face:background_job`, and the resulting list is written to the log at debug level. That check is what tells you which image formats your installation can actually analyse, which is the subject of the next section, because the answer is not simply what the server's image library supports.
The deliberate decision to leave out GIF, BMP and WebP
The list of supported formats is short, and the exclusions are explained, which is the kind of detail that tells you the project has been used on real libraries.
JPEG and PNG are analysed on any setup. On top of those, HEIC and HEIF, TIFF and AVIF are analysed when the image backend of your server can decode them. That happens in one of two ways. Decoding the images locally, the three depend on the Imagick extension when it was built with the proper delegates, which are libheif, libtiff and libavif. With an Imaginary service, configured with the `preview_imaginary_url` key, the three are analysed.
That two-path support is the practical detail. A Nextcloud instance that stores HEIC photos from phones usually has Imagick without the HEIF delegate, or with it, and the difference decides whether your newest photos are analysed at all. The Imaginary service path exists for servers that do not have those delegates compiled in.
Then the exclusions, which are the interesting part. GIF, BMP and WebP are deliberately left out, even though the local backends read them. The reason given is that they are the formats of memes, stickers and screenshots, so analysing them mostly fills the results with the faces of drawings and video captures, which you then have to reject by hand.
That is a good reason and a good kind of engineering decision. A face recognition system that surfaces a dozen drawings of the same cartoon character as a separate person, which then appears in your friends list and has to be renamed away, is worse than one that never found it. The trade is that some genuine photographs in those formats are also skipped, and the mitigation is the `enabledFaceRecognitionMimetype` array in `config.php`, where you can force additional mimetypes to be analysed, with GIF and WebP named as examples.
So the format list is not a limitation to work around, it is a default that you can adjust per instance, and the reasoning is about the quality of the results rather than about the capabilities of the decoder.
Consent, the user interface and the state of the project
The consent model is explicit and it is worth understanding, because it is the part of the design that a cloud service cannot offer.
The administrator must properly configure the application, and once it is working, the user must accept that they want to allow the analysis of their images to discover their friends. So the administrator can enable the app, but the analysis of a given user's photos needs that user's own acceptance. The user settings then have a Face Recognition panel where each user must enable the analysis, and once enabled you progressively see the discovery of your friends, and you can assign them names.
The grouping is by similarity, and the naming is manual, which is the honest design. An automatic system that names a cluster for you is usually wrong often enough to be annoying, so this one leaves the labels to you and makes the clusters easy to browse.
The user-facing surface is in three places. The user settings panel, where the analysis is enabled and names are assigned. The Files application, where you can search by typing your friend's name and it will show all the photos. And a Persons tab in the side panel of both the Files application and the Photos application, where you see the list of your friends in the photos, rename them, and in the Files case select the folders you want to ignore for the process.
That folder exclusion is a useful detail, because a shared folder full of group photos or a holiday album is exactly where the false matches accumulate.
The project state is worth a note. The recorded releases are v0.9.70 from 2025-04-22, v0.9.60 from 2024-11-19 and v0.9.51 from 2024-06-18, while the last push to the repository was on 2026-10-18, so the branch is active. The package manifest says version 0.6.0, which is a different numbering scheme from the app releases, since the manifest version tracks the JavaScript package and the releases track the Nextcloud app. The quality tooling in the repository is thorough, with a PHPUnit workflow, Scrutinizer badges for quality and coverage, a Codacy grade, a psalm configuration for static analysis, a stylelint config and a `.tx/` directory for Transifex translations, which is the standard arrangement for a Nextcloud app that wants to be translated by its community.
Editorial conclusion
Face Recognition suits a Nextcloud operator who wants photo face grouping without uploading a private library to a cloud service, and who can afford to run a scheduled analysis job on a server with a gigabyte of memory to spare. It does not suit an instance that cannot install PHP extensions, since dlib bindings are required, and it does not suit a large library analysed on a shared host. Verify first that the models are installed with `occ face:setup`, that the memory and temporary file settings match the size of the images you process, and that the test run with `occ face:background_job -u new_user -t 900` gives usable results on your own data before you point it at a real library.
Frequently asked questions
What does the Nextcloud Face Recognition app do?
It recognizes, analyzes and aggregates face data in the images already stored on your Nextcloud instance, grouping detected faces by similarity so you can name them and search photos by person. The images never leave the instance, which is the property that distinguishes it from a cloud photo service.
What are the requirements for installing the Face Recognition app?
Nextcloud 34, the Dlib PHP bindings from the pdlib project, the PHP Bzip2 extension and 1GB of RAM. The dlib bindings are a native extension you compile against your PHP build, so the app cannot be installed on a host where you cannot load your own extensions.
How do I build the Face Recognition app from source?
Clone the repository into the Nextcloud apps directory and run make. The command installs the PHP and JavaScript dependencies, builds the Vue.js components with webpack, copies the vendor JavaScript libraries and compiles the Handlebars templates, and running only npm run build is explicitly not sufficient.
How do I run the face analysis on a Nextcloud instance?
Install the pretrained models first with the occ face:setup command, then run occ face:background_job for a user, for example occ face:background_job -u new_user -t 900, where the time limit lets the job process the queue in bounded slices. The documentation recommends testing on a new user with a few uploaded photos before running it on real data.
Why are GIF, BMP and WebP images not analysed?
They are deliberately excluded because they are the formats of memes, stickers and screenshots, so analysing them mostly produces faces of drawings and video captures that you then have to reject by hand. You can force additional mimetypes through the enabledFaceRecognitionMimetype array in config.php.
Official sources
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.
[](https://hysenlabs.com/projects/matiasdelellis-facerecognition)Was this useful?