Self-hosted service
nextcloud/server avatar
nextcloud/server

Nextcloud server: a checkout that needs submodules, four test runners, and no install command

Nextcloud server, a safe home for all your data. Do you want to learn more about how you can use Nextcloud to access, share, and protect your files, calendars, contacts, communication & more at home and in your organization?

36,921 stars5,248 forksPHPAGPL-3.0

At a glance

What is it?
The nextcloud/server repository is a development tree, not a product you can unzip. Third-party code arrives as submodules, the default branch is missing two apps, four test frameworks run against it, and the root README carries no install command at all.
Who is it for?
Nextcloud server suits teams that intend to develop apps or manage their own infrastructure, because the repository is built for contributors and its default branch is explicitly incomplete. Anyone expecting a ready deployment, or a set of apps that the checkout already contains, should install from the published packages or a preinstalled device instead.
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 6 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 September 25, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Third-party code arrives as submodules, so a plain clone is broken

The 3rdparty directory at the repository root is not ordinary source. It is wired up through .gitmodules, which means a regular `git checkout` leaves that directory empty and the PHP autoloader without the libraries it expects. The fix is the one command the README bothers to spell out:

code
git submodule update --init

or, in the project's own words, a similar command. The consequence for a reader is immediate and easy to misdiagnose: a fresh clone that skips this step will not fail with a message about submodules, it will fail with missing class errors from code that was never downloaded. The repository does carry .envrc, flake.nix, flake.lock, and a .devcontainer directory, so several environment entry points exist, and none of them removes the submodule requirement. Setup in this project means reading the developer manual linked from the contributing steps, not inferring it from the file tree.

The default branch is missing First run wizard and Activity

master is the working branch, and it is not the same thing as a release. Apps that ship in regular releases, including the first run wizard and Activity, are absent from master and have to be installed by hand:

code
git clone into the apps subfolder

So a checkout of master is a partially populated server. The onboarding flow that a fresh install would normally walk a user through is not in the tree, and the activity feed that many deployments depend on is not there either, which means a contributor testing changes against master sees different application behavior than a user on a release. The project offers the other side of the coin in the next sentence: the `stable*` branches can be handled the same way as release archives, but they should never be used on production systems. Both branches are therefore disqualified for different reasons, and picking the wrong one is a decision with real consequences.

Four test frameworks, one per layer, and no default test target

Rather than one runner, the project splits testing by area: PHPUnit for PHP unit tests, Behat for PHP integration tests, Vitest for JavaScript and TypeScript unit tests, and Playwright for end-to-end tests. The npm scripts map onto the JavaScript side only:

code
npm test
vitest run
npm run test:coverage
npm run test:update-snapshots
npm run playwright
playwright test --project=default --project=admin-settings

The end-to-end path has its own setup scripts, `npm run playwright:install` and `npm run playwright:setup`, and its own debugging guide at tests/playwright/README.md. Cross-browser coverage runs on BrowserStack, accessibility on WAVE, and performance and accessibility checks on Lighthouse. What you cannot get from this is one command that tells you the code is sound: a contributor runs the PHP runners, the JavaScript runner, and the browser suites as separate processes, and a passing Vitest run says nothing about whether Behat passes.

make all deletes dist before it builds anything

The Makefile is a thin wrapper over npm, and its first rule is a destructive one. The default target is `all: clean dev-setup build-js-production`, where clean runs `rm -rf dist`. Only `clean-git` puts the directory back, by checking it out of git. So the ordinary way to build the JavaScript bundle in this project begins by deleting the previously built one, and anyone who runs the default target expecting an incremental build loses the existing `dist` tree first. The rest of the file is a naming map: dev-setup calls `npm ci`, build-js calls `npm run dev`, build-js-production calls `npm run build`, lint-fix calls `npm run lint:fix`, and watch-js calls `npm run watch`. A `npm update` target sits next to `npm ci`, so the lockfile and the loose dependency update are both one make invocation away.

No CLA, individual copyright, and AGPL from June 2016 onward

Contributions carry a licensing policy that has a date in it. Everything contributed from June 16, 2016 onward is treated as AGPLv3 or any later version, matching the SPDX identifier in the source headers, which also credit ownCloud, Inc. for 2013 through 2016. A contributor license agreement is not required, and copyright belongs to the individual contributors, which is why substantial changes are expected to add a line to the AUTHORS file:

code
- <your name> <your email address>

The practical consequence is legal rather than technical, and it cuts both ways. There is no CLA to negotiate, which lowers the friction for outside contributions. The same policy means a company that patches this server and serves it to users is working inside a network copyleft regime and cannot treat the result as proprietary work, so a vendor evaluating a customized deployment needs an answer to that before the first patch, not after.

npm run lint is two passes, and the first one only covers Playwright

The lint script is a chain rather than a single tool. It runs eslint against ./tests/playwright with `--suppressions-location build/eslint-baseline.json` and `--no-error-on-unmatched-pattern`, then hands off to `build/demi.sh lint` in a postlint hook. Fixing is the same shape, with `npm run lint:fix` wrapping `concurrently` so the JavaScript and PHP passes run together, and `npm run lint:fix-watch` for the loop. The baseline file is the part worth noticing: it records existing violations, which means a clean lint run says the tree introduces no new findings, not that it has none. Underneath all of it sits build/demi.sh, the script that every build, dev, postinstall, and lint entry point calls, and that is where the real toolchain lives rather than in package.json.

Four routes to a running server, none of them a command in the root

Read this repository expecting install instructions and you will not find them. The four documented routes are a hosted signup with one of the providers, a self-installed server on your own hardware or through a ready-to-use appliance, a device that ships with Nextcloud preinstalled, and a service provider who hosts it for you. Each has a different cost: the signup moves your data to someone else's operation, self-installation puts the burden on you, appliances and devices shift that burden to a vendor image, and a provider is a recurring contract. Enterprise, public sector, and education users are pointed at a commercial offering. The repository itself contributes none of these, and the actual server setup commands live on the documentation site, so anyone who needs reproducible install steps documented in source will not find them in this tree.

Six steps to a pull request, and one bot for submodules only

The contribution path is spelled out as a numbered sequence: set up a local development environment, pick an issue labeled as a good first issue, create a branch and sign off commits with `git commit -sm "Your commit message"`, open a pull request that `@mention`s the people from the issue to ask for review, fix what review raises, and wait for the merge. One automation exists on the repository side, and its scope is narrow: commenting `/update-3rdparty` on a pull request moves the 3rd party submodule to the last commit of the branch matching the pull request target. Everything else waits on a human reviewer, which is why the repository also carries a .pre-commit-config.yaml, an eslint.config.js, and a .php-cs-fixer.dist.php that will judge your formatting before anyone reads the change.

Editorial conclusion

Nextcloud server suits teams that intend to develop apps or manage their own infrastructure, because the repository is built for contributors and its default branch is explicitly incomplete. Anyone expecting a ready deployment, or a set of apps that the checkout already contains, should install from the published packages or a preinstalled device instead. Before you commit, verify three things the repository cannot answer for you: which PHP version and database your target release supports, what the App Store apps require at install time, and how the AGPL obligations apply to a modified deployment you expose to users.

Frequently asked questions

What does a fresh Nextcloud server git checkout include?

It includes the server source in PHP, the apps that live in the apps directory, and a 3rdparty directory that stays empty until you run git submodule update --init, because the third-party components are handled as git submodules that have to be initialized first.

Why is the Activity app missing from a Nextcloud checkout?

Apps that are included by default in regular releases, such as the first run wizard and Activity, are missing in master and have to be installed manually by cloning them into the apps subfolder. A checkout of master is therefore a partially populated server.

Can I run a stable branch of Nextcloud on a production server?

No. The git checkouts can be handled the same as release archives by using the stable* branches, and the project states plainly that they should never be used on production systems, so those branches exist for development convenience only.

Which test frameworks does Nextcloud server use?

Four, split by area: PHPUnit for PHP unit tests, Behat for PHP integration tests, Vitest for JavaScript and TypeScript unit tests, and Playwright for end-to-end tests. The npm scripts cover the JavaScript side, with npm test running vitest run and npm run playwright running the Playwright projects.

Do Nextcloud contributors have to sign a CLA?

No. Nextcloud does not require a contributor license agreement, and copyright belongs to all the individual contributors. Contributions from June 16, 2016 onward are considered licensed under the AGPLv3 or any later version.

Official sources

  1. Official documentation
  2. Official README
  3. Project repository
  4. Release notes
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-server.svg)](https://hysenlabs.com/projects/nextcloud-server)