Multi-tenancy Global Config — loading per-domain $CONFIG on a single Nextcloud instance

Hi everyone,

We’d like to share a module we’re developing at LibreCode: [multitenancy-global-config]( GitHub - LibreCodeCoop/multitenancy-global-config: Multi-tenant global config loader for Nextcloud: resolves $CONFIG per request host from a domain config matrix · GitHub )**, which solves a common problem for people running Nextcloud in a multi-tenant setup.

The problem

A single Nextcloud instance serving multiple domains (e.g. `tenant01.example.coop`, `tenant02.example.coop`) often needs different settings per domain: database, SMTP, data directory, and so on. Nextcloud doesn’t provide a built-in, structured way to load configuration based on the request host.

How it works

Nextcloud already loads and merges every `config/*.config.php` file into the global `$CONFIG`. This module takes advantage of that:

- A tenant matrix lives in `config/multitenancy.database.php` (the filename intentionally doesn’t end in `config.php`, so Nextcloud won’t auto-load it).

- A loader, `config/multitenancy.config.php` (which *is* auto-loaded), reads the request host (`HTTP_HOST`) and passes it to `Manager::getConfigFromHost()`, which matches it against the matrix’s regex keys and returns the matching tenant config — or an empty array if nothing matches.

Matrix keys are full regular expressions (with delimiters), and the first match wins. Values are regular `$CONFIG` arrays, merged over the global config. Example:


$CONFIG = [

'/^domain01\.example\.coop$/' => [

'dbname' => 'tenant01',

'mail_smtphost' => 'smtp01.example.coop',

\],

];

Trust boundary

The `Host` header is fully client-controlled. That’s why the regex keys are anchored (`/^…$/`), preventing one tenant from selecting another tenant’s config. A host that matches no key falls through to the **base** config — so it’s important to configure your reverse proxy to only route known tenant hosts to this instance.

CLI (occ, cron, background jobs)

CLI processes have no `Host` header, so they fall back to `localhost`/base config by default. To run a command as a specific tenant, just set the environment variable:

HTTP_HOST=domain01.example.coop php occ maintenance:mode --on

Installation

The module is location-agnostic — you can clone it (or add it as a git submodule) anywhere the PHP process can read, inside or outside the Nextcloud webroot:

git clone GitHub - LibreCodeCoop/multitenancy-global-config: Multi-tenant global config loader for Nextcloud: resolves $CONFIG per request host from a domain config matrix · GitHub

1. Copy `examples/multitenancy.config.php` into your Nextcloud `config/` directory and adjust the `require_once` path to point to where you cloned the repo.

2. Create `config/multitenancy.database.php` with your tenant matrix (see `examples/multitenancy.database.php`).

The matrix filename can be overridden via the `MULTITENANCY_CONFIG_FILE` environment variable.

Licensed under AGPL-3.0-or-later. Feedback, testing, and contributions are very welcome!

:link: GitHub - LibreCodeCoop/multitenancy-global-config: Multi-tenant global config loader for Nextcloud: resolves $CONFIG per request host from a domain config matrix · GitHub

I don’t really get the advantage of such multi-tenancy config :thinking: are you trying to consolidate multiple instance into one to allow/improve collaboration or do you try to separate a bigger instance into smaller pieces?

if you are running separate infrastructure like DB, mail server and maybe storage already, the overhead of “another” webserver per domain sounds small. I have the feeling running a separate docker instance per domain is easier and more robust.

A bigger one with multiple independents instances.

Yes, is easier to separate with docker (sysadmin pov) and works well but the source code would be replicated (developers pov).

But using docker is a little catch because the entrypoint runs a rsync and could delete your /data folder if you have another /data_xpto in the directory. D: Some findings here.

there is no such thing.. Nextcloud consists of application, database and storage (LAMP stack).. each of them belong together and build one instance. there is no (good) way to separate only databases and run single storage.. or separate storage and run one database.

there is no “source code” in production.. if you mean application code and binaries - docker is really good in this terms.. the image is stored once on the system and each instance instantiated from this image only adds few bytes more storage.. but yes definitely cpu and memory requirements grow.. I don’t think the overhead is too much compared with a single system..

each container must have it’s own volumes/bind mounts - no chance instances access data over cross..

Hey, it’s just an deployment option.

Use one container (two,btw, needs cron) for each environment or setup multitenancy, you choose. Both have upsides and downsides.

If you have any contribution feel free to open a PR in the repository.