Installs and configures Nextcloud behind Apache with PHP-FPM and
a local Valkey cache, via the osl_nextcloud custom resource. It handles fresh installs, in-place
upgrades between Nextcloud versions, and adopting an existing (imported) database.
- AlmaLinux 9 (9.7+)
- AlmaLinux 10
9.7 is the first release with valkey in AppStream.
- Chef Infra Client >= 18.0
ark— downloads and extracts the Nextcloud release tarballosl-apache— Apache vhost (apache_app) andmod_remoteiposl-php— PHP install,php.ini, and PHP-FPM pool;osl_php_web_servicepoints php-fpm at osl-php's web-only ini directory (a 300smysqlnd.net_read_timeout, so workers recover from a MySQL failover;occand cron keep the CLI default) and reloads it when PHP configuration changesosl-repos— EPEL (and Alma) repositoriesosl-selinux— SELinux contexts and booleansosl-valkey(>= 2.0.0) — thevalkey@nextcloudcache instance
This cookbook exposes no user-facing node attributes; all configuration is done through
the osl_nextcloud resource. (It does set node['osl-apache']['mpm'] internally, and
node['osl-apache']['behind_loadbalancer'] from the behind_loadbalancer property.)
Installs and configures a complete Nextcloud instance. The default (and only) action is
:create.
| Property | Type | Default | Description |
|---|---|---|---|
server_name |
String | resource name | Site/vhost name (also the install path under /var/www). |
server_aliases |
Array | %w(localhost) |
Additional trusted domains / Apache aliases. |
version |
String | '33' |
Nextcloud version to deploy. A bare major ('33') resolves to the latest release via GitHub; an exact version ('25.0.13') is used as-is (required for older releases). |
checksum |
String | (auto) | sha256 of the release tarball. Fetched from nextcloud.com if unset. |
php_version |
String | '8.3' |
PHP version to install. |
php_packages |
Array | [] |
Extra PHP packages to install beyond the defaults. |
max_filesize |
String | '1G' |
upload_max_filesize / post_max_size. |
redis_port |
Integer | 6379 |
Port valkey@nextcloud listens on, and Nextcloud's redis port. Override when 6379 conflicts with another service. |
valkey_maxmemory |
String | '512mb' |
maxmemory for valkey@nextcloud. |
database_host |
String (sensitive) | — | Required. Database host. |
database_name |
String | — | Required. Database name. |
database_user |
String (sensitive) | — | Required. Database user. |
database_password |
String (sensitive) | — | Required. Database password. |
nextcloud_admin_user |
String | 'admin' |
Admin account created on a fresh install. |
nextcloud_admin_password |
String (sensitive) | — | Required. Admin password. |
mail_domain |
String | — | Required. Mail domain (mail_domain). |
mail_smtphost |
String | 'smtp.osuosl.org' |
SMTP host. |
mail_from_address |
String | 'noreply' |
From address. |
apps |
Array | [] |
Apps to app:install and app:enable. |
apps_disable |
Array | [] |
Apps to app:disable. |
behind_loadbalancer |
true/false | true |
Pin overwriteprotocol to https. Nextcloud builds absolute URLs (notably the post-login redirect Location) from the scheme it detects on the request; with TLS terminating at the load balancer the backend connection is plain HTTP, so without this the redirect is emitted as http:// and browsers block it under the login page's CSP form-action 'self'. |
extra_config |
Hash | {} |
Extra config:system:set values. Ruby booleans/integers are written with the matching --type=boolean/--type=integer; strings are written untyped. |
maintenance_window_start |
Integer | 1 |
maintenance_window_start (UTC hour). |
instance_id |
String (sensitive) | — | Adopt mode: instanceid of an existing instance. |
password_salt |
String (sensitive) | — | Adopt mode: passwordsalt of an existing instance. |
secret |
String (sensitive) | — | Adopt mode: secret of an existing instance. |
source_version |
String | — | Adopt mode: version recorded in the imported database's original config.php (see below). |
sensitive |
true/false | true |
Mask sensitive command output in logs. |
osl_nextcloud 'nextcloud.example.com' do
server_aliases %w(localhost nextcloud.example.com)
database_host 'db.example.com'
database_name 'nextcloud'
database_user 'nextcloud'
database_password 'super-secret'
nextcloud_admin_password 'unguessable'
mail_domain 'example.com'
php_packages %w(ldap)
apps %w(forms)
extra_config(
'default_phone_region' => 'US',
'allow_user_to_change_display_name' => false # written with --type=boolean
)
endNextcloud's distributed cache (memcache.distributed) runs on an osl-valkey instance,
valkey@nextcloud, bound to 127.0.0.1 on redis_port. It has no persistence and uses
allkeys-lru eviction, since every key in it is disposable. osl-valkey restarts it on
failure, and osl-prometheus's redis_exporter finds and monitors it on its own
(valkey_instance="nextcloud", ValkeyDown). The instance is always named nextcloud, so
several osl_nextcloud resources on one host share it and must agree on redis_port and
valkey_maxmemory; osl-valkey fails the converge if they differ.
Earlier releases ran the packaged redis.service (EL9) or valkey.service (EL10) on the
same port instead. The resource no longer manages that server, so on a host that had one,
stop and disable it immediately before the first converge with valkey@nextcloud; the new
instance cannot bind the port while the old server holds it.
This cookbook ships no recipes; it provides the osl_nextcloud resource for use in a
wrapper/consumer cookbook. The recipes under test/cookbooks/nextcloud-test exercise the
resource for Test Kitchen and ChefSpec.
osl_nextcloud can adopt an existing instance's database instead of running a fresh
maintenance:install. Supply the identity from the source instance's config.php
(typically sourced from a data bag) and the resource bootstraps config.php from it,
skips maintenance:install, and lets occ upgrade migrate the imported schema:
osl_nextcloud 'files.example.org' do
database_host '...'
database_name '...'
database_user '...'
database_password '...'
nextcloud_admin_password '...'
mail_domain 'example.org'
# All three are required to enable "adopt existing instance" mode.
instance_id dbcreds['instanceid']
password_salt dbcreds['passwordsalt']
secret dbcreds['secret']
endWhen all three of instance_id, password_salt, and secret are set, the resource is in
adopt mode. Leave them unset for a normal fresh install.
Nextcloud only upgrades one major version at a time, and each major supports a
different range of PHP. Because this cookbook deploys a single target version, you cannot
adopt a database more than one major behind the deployed code — occ upgrade refuses
multi-major jumps. To migrate an old instance (e.g. 25.x) up to the deployed major (e.g.
33.x), step its database through each major in a staging VM first, then adopt the result.
The upgrade Test Kitchen suite and test/upgrade-chain.sh automate this:
-
Place the source database dump at
test/cookbooks/nextcloud-test/files/nextcloud-source.sql(git-ignored). Only the database is needed —occ upgrademigrates the schema and does not read the file data directory. -
Export the source instance's identity and its current version (read from its old
config.php):export NC_INSTANCEID=... NC_SALT=... NC_SECRET=... export NC_SOURCE_VERSION=25.0.7 # the `version` key from the source config.php
-
Review the version/PHP steps in
test/upgrade-chain.shand confirm each exact patch release exists at https://download.nextcloud.com/server/releases/, then run it from the cookbook root:test/upgrade-chain.sh upgrade-almalinux-9
Up front, the script fails fast if it isn't run from the cookbook root, if the source dump
is missing, or if any of the four required environment variables are unset/empty (it
reports all the missing ones at once). It then prompts for confirmation before destroying
the instance — set NC_YES=1 to skip the prompt in automation.
After confirming, it destroys the instance and converges the upgrade suite once per major
(PHP 8.1 covers Nextcloud 25–31; 32 and 33 need PHP 8.3), letting occ upgrade migrate the
schema at each step. Each major is a single converge — the upgrade suite disables the
idempotency check, since every step is a deliberate one-way change. On the first converge it
stamps source_version so occ upgrades from the imported database's real baseline; later
steps clear it. Finally it SSHes in, mysqldumps the migrated database to
nextcloud-migrated.sql, and verifies the dump looks complete. Adopt that dump in
production at the target version (with source_version left unset).
If a step fails, fix the cause and resume from where it stopped instead of rebuilding from
scratch — the last completed step is recorded in .upgrade-chain-<instance>.state:
NC_RESUME=1 test/upgrade-chain.sh upgrade-almalinux-9NC_RESUME=1 skips the destroy and continues from the next step. The resource also
re-runs occ upgrade whenever config.php's recorded version is behind the deployed code,
so resuming a step whose upgrade failed partway retries it (rather than silently skipping it
because the tarball is already extracted).
The source_version property is what makes this work: it records the version of the
imported database so occ upgrade runs from the correct baseline. Set it when the
database's version differs from the deployed code; leave it unset for same-version
adoption (where the deployed code's version is used).
Every run is logged to a timestamped file under test/upgrade-logs/ (the .kitchen/logs
files are truncated each kitchen action). At the end the script writes a filtered
.warnings sidecar of actionable WARN:/ERROR:/FATAL: lines, dropping known-benign
noise (chef built-in resource overrides, ohai dmidecode, symlink notices, the test-only
missing apache htpasswds data bag, etc.). The full, unfiltered output stays in the .log.
When staging an LDAP-backed instance you will see repeated
- ERROR: Lost connection to LDAP server. lines. These are expected and safe to ignore
in staging: the kitchen VM can't reach the LDAP server, so a few occ maintenance:repair
steps that consult the group backend (e.g. "Remove shares of old group memberships") can't
run. They are post-migration cleanups, not schema work — the schema migration itself is
DB-only and completes — and the step fails safe (it aborts without modifying shares when it
can't verify membership), so the dump is intact. Most other repair steps only queue
background jobs, which run later via cron.php.
You do not need a separate manual upgrade on production. The production adopt converge
re-runs occ maintenance:repair --include-expensive itself (ark extracts the tarball, which
notifies upgrade-nextcloud), and there LDAP is reachable, so those steps complete. After
the production import + first converge, just verify:
- The converge log shows
occ maintenance:repairran without "Lost connection to LDAP" (optionally runsudo -u apache php occ maintenance:repair --include-expensiveonce and confirm it's clean). - The
apachecron is active (crontab -l -u apache) so the queued background jobs run. sudo -u apache php occ files:scan --allfor storage paths, per the migration handoff.
cinc exec rspec # ChefSpec unit tests
cinc exec cookstyle # lintTest Kitchen suites:
default— fresh install.migrate— adopts a same-version database dump (nextcloud-fixture.sql).upgrade— staging chain for stepping an old database up across majors (see above).
- Fork the repository on Github
- Create a named feature branch (like
username/add_component_x) - Write tests for your change
- Write your change
- Run the tests, ensuring they all pass
- Submit a Pull Request using Github
- Author:: Oregon State University chef@osuosl.org
Copyright:: 2022, Oregon State University
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.