NEXTCLOUD(7) Miscellaneous Information Manual NEXTCLOUD(7)

Nextcloud on OpenBSDinstalling Nextcloud on OpenBSD

The instructions below will guide you through installing and updating Nextcloud on a computer running OpenBSD 7.0 or greater. The instructions assume the reader is starting from a fresh OpenBSD installation.

  1. httpd: first steps
  2. httpd with tls
  3. postgresql
  4. php
  5. redis
  6. nextcloud
  7. cron jobs
  8. references

OpenBSD comes with a built-in webserver called httpd(8). It is a rather basic webserver that supports FastCGI and TLS. There is a config file located in /etc/examples that we can use as a starting point:

$ doas cp /etc/examples/httpd.conf /etc/

First, let's configure the http daemon so that we can get a simple "hello, world!" webpage working. Edit /etc/httpd.conf, replace example.com with your own domain.tld, and comment out the TLS section. At the end, it should look like this:

server "domain.tld" {
	listen on * port 80

	location "/.well-known/acme-challenge/*" {
		root "/acme"
		request strip 2
	}

	location * {
		block return 302 "https://$HTTP_HOST$REQUEST_URI"
	}
}

# server "domain.tld" {
# 	listen on * tls port 443
# 	tls {
# 		certificate "/etc/ssl/example.com.fullchain.pem"
# 		key "/etc/ssl/private/example.com.key"
# 	}
# 	location "/pub/*" {
# 		directory auto index
# 	}
# 	location "/.well-known/acme-challenge/*" {
# 		root "/acme"
# 		request strip 2
# 	}
# }

Check that the configuration is valid:

$ doas httpd -n

If no errors return, create the hypertext documents (i.e., htdocs) root folder and create a dummy file called index.html with the text "hello, world!" in it:

$ doas mkdir -p /var/www/htdocs/www.domain.tld
$ doas echo "hello, world!" > /var/www/htdocs/www.domain.tld/index.html
Now configure your router to forward port 80 (note: this is out of the scope of this tutorial) and see if you can navigate to your website.

On OpenBSD, httpd(8) runs in a chroot environment. You must copy over resolver and SSL config files into this environment so that the webserver has no difficulty resolving DNS queries and managing HTTPS certificates:

$ doas mkdir -p /var/www/etc/ssl
$ doas cp /etc/ssl/cert.pem /var/www/etc/ssl/
$ doas cp /etc/ssl/openssl.cnf /var/www/etc/ssl/
$ doas cp /etc/resolv.conf /var/www/etc/
$ doas chown -R www:www /var/www/etc
Now prepare acme-client(1) to handle the SSL certificates:
$ doas mkdir -p -m 700 /etc/acme
$ doas mkdir -p -m 700 /etc/ssl/private
$ doas mkdir -p -m 700 /etc/ssl/acme/private
$ daos mkdir -p -m 755 /var/www/acme
In this config we will rely on Let's Encrypt, which is a non-profit certificate authority that provides free TLS certificates. Create and edit /etc/acme-client.conf, replacing domain with your hostname:
authority letsencrypt {
	api url "https://acme-v02.api.letsencrypt.org/directory"
	account key "/etc/acme/letsencrypt-privkey.pem"
}

authority letsencrypt-staging {
	api url "https://acme-staging-v02.api.letsencrypt.org/directory"
	account key "/etc/acme/letsencrypt-staging-privkey.pem"
}

domain domain {
	alternative names { www.domain, git.domain cloud.domain mail.domain }
	domain key "/etc/ssl/private/domain.key"
	domain certificate "/etc/ssl/domain.crt"
	domain full chain certificate "/etc/ssl/domain.pem"
	sign with letsencrypt
}
Now restart the http daemon and generate a certificate for your domain, and use it to provide HTTPS:
$ doas rcctl restart httpd
$ doas acme-client -v domain.tld
If that was successful, use ocspcheck(8) to check the certificate for validity against its Online Certificate Status Protocol (OCSP) responder:
$ doas ocspcheck -N -o /etc/ssl/<domain.tld>.ocsp.pem /etc/ssl/<domain.tld>.fullchain.pem
$ doas rcctl restart httpd
If you received no errors, congratulations, you have TLS working correctly. Open /etc/httpd.conf using your favorite editor and modify the file based on Nextcloud's recommendations. Below, I have shared a rather complex configuration example that handles multiple subdomains. using this configuration, one can access Nextcloud only by navigating to https://xyz.domain.tld/nextcloud. Note that the strings $DOCUMENT_URI and $SERVER_NAME should not be replaced with anything -- they are variables interpreted by httpd(8).
# [ SUBDOMAINS ]
### https://domain.tld ###
server "domain.tld" {
	listen on * tls port 443
	root "/htdocs/www.domain.tld/"

	tls {
		certificate "/etc/ssl/domain.tld.fullchain.pem"
		key "/etc/ssl/private/domain.tld.key"
	}

	location "/.well-known/acme-challenge/*" {
		root "/acme"
		request strip 2
	}
}

### https://www.domain.tld ###
server "www.domain.tld" {
	listen on * tls port 443
	root "/htdocs/www.domain.tld/"

	tls {
		certificate "/etc/ssl/domain.tld.fullchain.pem"
		key "/etc/ssl/private/domain.tld.key"
	}

	location "/.well-known/acme-challenge/*" {
		root "/acme"
		request strip 2
	}
}

### https://abc.domain.tld ###
server "abc.domain.tld" {
	listen on * tls port 443
	root "/htdocs/abc.domain.tld/"

	tls {
		certificate "/etc/ssl/domain.tld.fullchain.pem"
		key "/etc/ssl/private/domain.tld.key"
	}

	location "/.well-known/acme-challenge/*" {
		root "/acme"
		request strip 2
	}
}

### https://xyz.domain.tld ###
server "xyz.domain.tld" {
	listen on * tls port 443

	location "/.well-known/acme-challenge/*" {
		root "/acme"
		request strip 2
	}


	# set root directory
	root "/nextcloud"
	directory index "index.php"

	# enable HTTP Strict Transport Security
	hsts {
		preload
		subdomains
		max-age 15768000
	}

	tls {
		certificate "/etc/ssl/domain.tld.fullchain.pem"
		key "/etc/ssl/private/domain.tld.key"
	}

	# set max upload size to 513M (in bytes)
	connection max request body 537919488
	connection max requests 1000
	connection request timeout 3600
	connection timeout 3600

	block drop

	# ensure that no "*.php*" files can be fetched from these directories
	location "/nextcloud/config/*" { block drop }
	location "/nextcloud/data/*" { block drop }
	location "/nextcloud/*.php*" {
		root "/nextcloud"
		request strip 1
		fastcgi socket "/run/php-fpm.sock"
		pass
	}

	location "/nextcloud/apps/*" {
		root "/nextcloud"
		request strip 1
		pass
	}

	location "/nextcloud/core/*" {
		root "/nextcloud"
		request strip 1
		pass
	}

	location "/nextcloud/updater/*" {
		root "/nextcloud"
		request strip 1
		pass
	}

	location "/nextcloud" { block return 301 "$DOCUMENT_URI/index.php" }
	location "/nextcloud/" { block return 301 "$DOCUMENT_URI/index.php" }
	location "/.well-known/carddav" { block return 301 "https://$SERVER_NAME/nextcloud/remote.php/dav" }
	location "/.well-known/caldav" { block return 301 "https://$SERVER_NAME/nextcloud/remote.php/dav" }
	location "/.well-known/webfinger" { block return 301 "https://$SERVER_NAME/nextcloud/public.php?service=webfinger" }
	location "/.well-known/webfinger" { block return 301 "/nextcloud/index.php/.well-known/webfinger" }
	location "/.well-known/nodeinfo" { block return 301 "/nextcloud/index.php/.well-known/nodeinfo" }

	location match "/nextcloud/oc[ms]%-provider/*" {
		block return 301 "$DOCUMENT_URI/index.php"
	}
}

Nextcloud requires a SQL database to store administrative data and in this tutorial we will install and configure PostgreSQL. This step is rather complicated because it involves juggling between different users. In particular, OpenBSD creates a new user called _postgresql, we will initialize the PostgreSQL database with a superuser called postgres, and within the database we will create a Nextcloud-specific user called nextcloud. Furthermore, you must create passwords for the latter two accounts.

Please consult /usr/local/share/doc/pkg-readmes/postgresql-server for further details. To install PostgreSQL and initialize the database, perform the following steps:

$ doas pkg_add postgresql-server
$ doas su - _postgresql
$ mkdir /var/postgresql/data
$ initdb -D /var/postgresql/data -U postgres -A md5 -W

The files belonging to this database system will be owned by user "_postgresql".
This user must also own the server process.

The database cluster will be initialized with locale "C".
The default database encoding has accordingly been set to "SQL_ASCII".
The default text search configuration will be set to "english".

Data page checksums are disabled.

Enter new superuser password:
Enter it again:

fixing permissions on existing directory /var/postgresql/data ... ok
creating subdirectories ... ok
selecting dynamic shared memory implementation ... posix
selecting default max_connections ... 20
selecting default shared_buffers ... 128MB
selecting default time zone ... America/Denver
creating configuration files ... ok
running bootstrap script ... ok
performing post-bootstrap initialization ... ok
syncing data to disk ... ok

Success. You can now start the database server using:

    pg_ctl -D /var/postgresql/data/ -l logfile start

$ pg_ctl -D /var/postgresql/data -l logfile start
$ psql -U postgres
Password for user postgres:
postgres=# CREATE USER nextcloud WITH PASSWORD 'password';
postgres=# CREATE DATABASE nextcloud;
postgres=# GRANT ALL PRIVILEGES ON DATABASE nextcloud TO nextcloud;
postgres=# q

$ exit
$ doas chown -R _postgresql:_postgresql /var/postgresql
$ doas rcctl enable postgresql && doas rcctl start postgresql

You must install several PHP packages if you want access to all of the various features that Nextcloud provides. As of this writing, Nextcloud only works with php-7.4, so make sure if you are given an option to select that version. Install them now:

$ doas pkg_add php php-pdo_pgsql
$ doas pkg_add php-curl php-gd php-intl php-zip
Make sure that /etc/php-7.4/ exists, and then enable the PHP modules like so:
$ doas sh -c 'for i in /etc/php-7.4.sample/*; do ln -sf /etc/php-7.4.sample/$i /etc/php-7.4/; done'

Finally, the PHP opcode cache module can be used to enhance performance. Open /etc/php-7.4.ini using your favorite editor and make sure the following options are set:

expose_php = Off
memory_limit = -1
max_input_time = 180
upload_max_filesize = 16G
post_max_size = 32M
opcache.enable=1
opcache.enable_cli=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=10000
opcache.revalidate_freq=1
opcache.save_comments = 1
If you wish to save bandwidth, you can decrease the upload_max_file to something like 512M.

Now enable and start the PHP FastCGI Process Manager (php-fpm):

$ doas rcctl enable php74_fpm
$ doas rcctl start php74_fpm

Nextcloud uses Redis for file caching and locking. Install, enable and start Redis as follows:

$ doas pkg_add redis pecl74-redis
$ doas rcctl enable redis
$ doas rcctl start redis

Though OpenBSD provides up-to-date packages for Nextcloud, it can be easier to download and unpackage the source directly. Download and verify the most recent version of Nextcloud like this:

$ ftp https://download.nextcloud.com/server/releases/nextcloud-23.0.0.zip
$ ftp https://download.nextcloud.com/server/releases/nextcloud-23.0.0.zip.asc
$ gpg2 --fetch-keys https://nextcloud.com/nextcloud.asc
$ gpg2 --verify nextcloud-23.0.0.zip.asc
$ doas unzip -d /var/www/ nextcloud-23.0.0.zip
$ doas chown -R www:www /var/www/nextcloud
$ doas touch /var/www/nextcloud/config/CAN_INSTALL
$ rm nextcloud-23.0.0.zip
Since Nextcloud is mostly just a collection of PHP files, you should be able to point your browser to your subdomain and be greeted with a Nextcloud installation wizard.

The installation wizard will ask you to create a Nextcloud admin (which is distinct from the PostgreSQL users you created above). However, you will use the nextcloud user and password you creative from above. Ignore the port number.

Data directory:		nextcloud/data
Database type:		PostgreSQL
Database user:		nextcloud
Database password:	'password'
Database name:		nextcloud
Database host:		localhost
Because PHP appends the chroot path /var/www to any command, combined with the fact that Nextcloud will run from within a chrooted environment, you must make a slight adjustment to prevent the maintenance cron jobs (discussed below) from failing. Create and edit /var/www/nextcloud/config/custom.config.php to look like this:
<?php
$CONFIG = array (
´datadirectory' => ((php_sapi_name() == 'cli') ? '/var/www' : '') . '/nextcloud/data',
);
Now open /var/www/nextcloud/config/config.php using your favorite editor and remove the "datadirectory" line, which will prevent Nextcloud from overwriting this custom workaround.

While you are in this file, make the following edits:

´memcache.local' => 'mcache
´memcache.locking' => 'mcache
´redis' => array(
  'host' => '127.0.0.1',
  'port' => 6379,
),


´filelocking.enabled' => true,
´memcache.local' => 'mcache
´memcache.locking' => 'mcache
´redis' => array(
  'host' => 'localhost',
  'port' => 6379,
  'timeout' => 0.0,
  'password' => '', // Optional, if not defined no password will be used.
),

Finally, create the following cron jobs to help run regular maintenance jobs and keep your SSL certificates up-to-date:

$ doas crontab -e
*/5  *  *  *  * php -f /var/www/nextcloud/cron.php
0 0 * * * acme-client <domain.tld> && rcctl reload httpd
0 * * * * ocspcheck -N -o /etc/ssl/<domain.tld>.ocsp.pem  /etc/ssl/<domain.tld>.fullchain.pem && rcctl reload httpd

  1. https://github.com/nixbitcoin/OpenBSD-Nextcloud
  2. https://github.com/crhenr/openbsd-selfhosted/blob/master/setup.sh
  3. https://www.h3artbl33d.nl/blog/nextcloud-on-openbsd
  4. https://docs.nextcloud.com/server/latest/admin_manual/installation/example_openbsd.html
  5. https://www.rohlix.eu/post/openbsd-nextcloud/+&cd=4&hl=en&ct=clnk&gl=us&client=firefox-b-1-d
December 28, 2021 dfdx