Nextcloud 12 From Scratch With FreeBSD 11

Here's a complete tutorial on installing Nextcloud 12 on FreeBSD 11.1. pkg actually includes nextcloud12, but it uses PHP 5.6, MySQL and Apache - I would much rather use PHP 7.1, PostgreSQL and nginx.

When we're done, you'll have a production-ready (at least by most home-user standards) Nextcloud installation running on nginx + PHP-FPM backend, with Redis-backed caching and SSL encryption courtesy of Let's Encrypt running on the best server OS money can't buy.

Nextcloud is a feature-rich system with many different components and installing it completely from scratch will give us a good understanding of it's inner workings, so let's get cooking...

Getting Started

Let's begin by installing our web and database servers. Notice we're installing Postgres 9.5, not 9.6 - this is because, at the time of this writing, the PHP PDO module for Postgres requires version 9.5:

$ sudo -s
Password:
# pkg install nginx postgresql95-server
Updating FreeBSD repository catalogue...
FreeBSD repository is up to date.
All repositories are up to date.
Updating database digests format: 100%
The following 4 package(s) will be affected (of 0 checked):

New packages to be INSTALLED:
nginx: 1.12.1,2
postgresql95-server: 9.5.8_1
libxml2: 2.9.4
postgresql95-client: 9.5.8_1

Number of packages to be installed: 4

The process will require 34 MiB more space.
7 MiB to be downloaded.

Proceed with this action? [y/N]: y

Assuming all went well, it's time initialise the database server:

# sudo -u pgsql initdb -D /usr/local/pgsql/data

Next, let's set up the services (note special Bash syntax):

# sysrc {ntpdate,nginx,postgresql,php_fpm}_enable=YES
# sysrc ntpdate_hosts=fi.pool.ntp.org

Notice I've also enabled network date/time synchronisation. Incorrect date/time will result in spurious authentication issues in Nextcloud. Just pick the right server pool.

Finally, let's fire up the database server and create the database we're going to use for Nextcloud. I'm using "nextcloud" as an example as the new Postgres user and database name:

# service postgresql start
LOG:  ending log output to stderr
HINT:  Future log output will go to log destination "syslog".
# createuser -U pgsql nextcloud
# createdb -U pgsql nextcloud -O nextcloud

Installing PHP

Next, it's time to install PHP and it's required extensions. The list of extensions differs from the one in the installation guide, but trust me - you need all of this stuff. Please note that I'm using Bash as my shell:

# pkg install php71 php71-{ftp,ctype,dom,gd,iconv,json,xml,mbstring,posix,simplexml,xmlreader,xmlwriter,zip,zlib,session,hash,filter,opcache,pdo_pgsql,curl,openssl,fileinfo}
Updating FreeBSD repository catalogue...
FreeBSD repository is up to date.
All repositories are up to date.
The following 37 package(s) will be affected (of 0 checked):

New packages to be INSTALLED:
php71: 7.1.9
    php71-ftp: 7.1.9
php71-ctype: 7.1.9
php71-dom: 7.1.9
php71-gd: 7.1.9
php71-iconv: 7.1.9
php71-json: 7.1.9
php71-xml: 7.1.9
php71-mbstring: 7.1.9
php71-posix: 7.1.9
php71-simplexml: 7.1.9
php71-xmlreader: 7.1.9
php71-xmlwriter: 7.1.9
php71-zip: 7.1.9
php71-zlib: 7.1.9
php71-session: 7.1.9
php71-hash: 7.1.9
php71-filter: 7.1.9
php71-opcache: 7.1.9
libXpm: 3.5.12
xproto: 7.0.31
libXext: 1.3.3_1,1
xextproto: 7.3.0
libXau: 1.0.8_3
libX11: 1.6.5,1
libxcb: 1.12_2
libXdmcp: 1.1.2
libpthread-stubs: 0.4
kbproto: 1.0.7
libXt: 1.1.5,1
libSM: 1.2.2_3,1
libICE: 1.0.9_1,1
freetype2: 2.8
png: 1.6.29
jpeg-turbo: 1.5.1
libiconv: 1.14_10
oniguruma6: 6.4.0
libzip: 1.1.3

Number of packages to be installed: 37

The process will require 49 MiB more space.
9 MiB to be downloaded.

Proceed with this action? [y/N]: y

Configuring PHP

Let's massage our new PHP installation to work with Nextcloud. First, we need to make PHP-FPM expose some environment variables. You could define each variable separately, but since this particular server will only be used for Nextcloud, with no other shared hosting accounts, I will make mine just expose everything:

# nano /usr/local/etc/php-fpm.d/www.conf

Uncomment the following line:

clear_env = no

Second, we need to make some changes to PHP's main configuration file, php.ini:

# cp /usr/local/etc/php.ini-production /usr/local/etc/php.ini
# nano /usr/local/etc/php.ini

Uncomment and update the following lines:

opcache.enable=1
opcache.enable_cli=1
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=10000
opcache.memory_consumption=128
opcache.save_comments=1
opcache.revalidate_freq=1

And bump the upload limit:

upload_max_filesize = 1G

With that, PHP and nginx should be good to go and you should be able to start the services:

# service php-fpm start
# service nginx start

Configure Caching

Since these shares might be shared amount multiple users, let's configure Redis as a caching backend to take advantage of transactional locking. For this we will need to install the Redis server and the corresponding PHP extension:

# pkg install redis autoconf
# sysrc redis_enable=YES
# service redis start

Unfortunately, pkg doesn't currently include the Redis module for PHP 7,1, so we'll have to install it manually from source:

# curl -O https://pecl.php.net/get/redis-3.1.4.tgz
# tar zxvf redis-3.1.4.tgz
# cd redis-3.1.4
# phpize
# ./configure && make && make test

If the compilation and testing was successful, install and activate:

# make install
# echo extension=redis.so >> /usr/local/etc/php/ext-20-redis.ini
# service php-fpm restart

Installing Nextcloud

With all the prerequisites in place, it's finally time to install Nextcloud 12:

$ curl -O https://download.nextcloud.com/server/releases/latest-12.tar.bz2
$ shasum -a 512 -c <(curl -fSsk https://download.nextcloud.com/server/releases/latest-12.tar.bz2.sha512)
latest-12.tar.bz2: OK
$ tar -zxvf latest-12.tar.bz2

Where do you want to put your new Nextcloud installation? Any path will work - here's just an example on a server with "ZFS on root". Creating a dedicated dataset for the system will allow us to set custom ZFS options as well as configure a dedicated snapshot schedule:

# zfs create zroot/nextcloud
# shopt -s dotglob nullglob
# mv nextcloud/* /zroot/nextcloud
# mkdir /zroot/nextcloud/data

The Nextcloud service needs to be able to write to that directory tree so let's give ownership to the www-user:

# chown -R www /zroot/nextcloud

Let's button up the Nextcloud config by activating it's cron script:

# sudo -u www crontab -e
*/15 * * * * /usr/local/bin/php -f /zroot/nextcloud/cron.php

Configuring nginx

The Nextcloud documentation has an excellent section on configuring nginx. I've shamelessly ripped their config which I'm using in the following examples:

# curl -o /usr/local/etc/nginx/nextcloud.conf https://gist.githubusercontent.com/filipp/6547dfe9524a1a05e49f69397ae9adff/raw/298b98e6f49f938fd8664f550b72ac1b3c671a55/nextcloud.conf
# nano /usr/local/etc/nginx/nextcloud.conf

I've marked the variables that you should change with a $-sign. Search and replace, then include the file in your nginx config:

# echo "include nextcloud.conf;" >> /usr/local/etc/nginx/nginx.conf

Check out this article from Calomel.org for some extra tips on optimising and securing your nginx install.

Adding Encryption

Let's finish off by adding SSL encryption courtesy of the fine folks at Let's encrypt:

# pkg install py27-certbot
Updating FreeBSD repository catalogue...
FreeBSD repository is up to date.
All repositories are up to date.
The following 25 package(s) will be affected (of 0 checked):

New packages to be INSTALLED:
py27-certbot: 0.16.0_1,1
py27-openssl: 16.2.0
py27-cryptography: 1.7.2
py27-ipaddress: 1.0.18
py27-setuptools: 36.0.1
py27-idna: 2.5
py27-six: 1.10.0
py27-pyasn1: 0.2.2
py27-enum34: 1.1.6
py27-cffi: 1.7.0
py27-pycparser: 2.10
py27-acme: 0.16.0,1
py27-requests: 2.18.1
py27-chardet: 3.0.3
py27-certifi: 2017.4.17
py27-urllib3: 1.21.1
py27-pysocks: 1.6.7
py27-pytz: 2016.10,1
py27-pyrfc3339: 1.0
py27-zope.interface: 4.1.3
py27-zope.component: 4.2.2
py27-zope.event: 4.1.0
py27-parsedatetime: 2.1
py27-configobj: 5.0.6_1
py27-configargparse: 0.12.0

Number of packages to be installed: 25

The process will require 23 MiB more space.
3 MiB to be downloaded.

Proceed with this action? [y/N]: y

Before we can request a cert, we need to disable the nginx directive that denies access to all paths that start with a full stop (like .well-known).

# nano /usr/local/etc/nginx/nextcloud.conf
location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console) {
  #deny all;
}

I will first experiment with the --staging environment since that won't throttle our connections in case we need to do this more then 5-times:

# certbot --staging certonly --webroot -w /zroot/nextcloud -d cloud.example.com

... if that worked OK, you can request a production cert by omitting the --staging switch. Also, don't forget to uncomment that deny all we just disabled!

HTH and good luck!