I recently setup Nextcloud 12 inside a FreeBSD jail in order to allow me access to files i might need while at University. I figured this would be a optimal solution for files that I might need access to unexpectedly, on computers where I am not in complete control. My Nextcloud instance is externally accessible, and yet if someone were to get inside my Jail, I could rest easy knowing they still didn’t have access to the rest of my host server. I chronicled the setup process including jail setup using iocage, https with Lets Encrypt, and full setup of the web stack.

Login screen for Nextcloud.
Login screen for Nextcloud.

Nextcloud has a variety of features such as calendar synchronization, email, collaborative editing, and even video conferencing. I haven’t had time to play with all these different offerings and have only utilized the file synchronization, but even if file sync is not needed, Nextcloud has many offerings that make it worth setting up.

On my latest install I am not making use of the file synchronization at all, I’ve attached external storage over NFS and am synchronizing the files using Syncthing. This lets me use my preferred sync client, but I’m still able to access all my files from the web interface or Nextcloud client. So far this has been the configuration I am most satisfied with.



Given that I wanted my Nextcloud install to be accessible from the internet, security was paramount as a consideration when planning the set up of my ‘cloud’. In order to maintain network security, and at the same time allow my Nextcloud to be open to the internet, I utilized a combination of tools.

My server that houses the install is in my DMZ, where very limited access is given to the rest of my network. I also put Nextloud inside a FreeBSD jail which is incredibly convenient. My Nextcloud instance is externally accessible, and yet if someone were to get inside my Jail, I could rest easy knowing they still didn’t have access to the rest of my host server. While I obviously wouldn’t rely on this fact alone, it does increase security greatly. It also means I can give the jail even less access to my network than my hosting server has.


In regards to configuration I went with the recommended setup for running Nextcloud.

  • MySQL/MariaDB
  • PHP 7.0 +
  • Apache 2.4 with mod_php

I would have liked to use Nginx and Postgres, but the majority of the documentation for setting up Nextcloud is for Apache - with only a small mention of Nginx, and no mention of Postgres. Not being an expert in either of the areas of database or webserver administration I went with the recommended settings.

jail Management

To manage my jails I’m using iocage. In terms of jail managers it’s a fairly new player in the game of jail management and is being very actively developed. It just had a full rewrite in Python, and while the code in the background might be different, the actual user interface has stayed very similar.

Iocage makes use of ZFS clones in order to create “base jails”, which allow for sharing of one set of system packages between multiple jails, reducing the amount of resources necessary. Alternatively, jails can be completely independent from each other; however, using a base jail makes it easier to update multiple jails as well.

A fork of the old version written in shell script, iocell can be installed from the FreeBSD ports tree as sysutils/iocell, or as a package with pkg install iocell.

The new version written in Python can be installed from the ports tree as either the Python2.7 version sysutils/py-iocage, or the Python3 version with sysutils/py3-iocage. Binary packages only exist for the Python2.7 version so to install the package run pkg install py27-iocage. As of this moment the Python 2.7 version is fairly old, so if using ports is a possibility, or you’re able to compile your own binary packages, the Python 3.6 version is more up-to-date.

Setup iocage

Enable iocage to autostart at boot:

[root]# sysrc iocage_enable=YES

Fetch the freebsd jail template.This will create the following datasets and then fetch and extract the requested release.

[root]# iocage fetch -r 11.0-RELEASE

Creating vault/iocage
Creating vault/iocage/download
Creating vault/iocage/images
Creating vault/iocage/jails
Creating vault/iocage/log
Creating vault/iocage/releases
Creating vault/iocage/templates

Fetching 11.0-RELEASE...

Create the jail

With the shared ip set up, create a new jail. In order to make it easier to keep track of different jails, a “tag” can be added. I called mine “stratus”:

[root]# iocage create tag="stratus" jail_zfs=on vnet=off ip4_addr="sge0|" -r 11.0-RELEASE

This creates a jail with the following properties.

  • tag=stratus - Set a tag for the jail to make it easier to refer to.
  • jail_zfs=on - Allow any assigned datasets to be controlled by the jail.
  • vnet=off - Do not use VIMAGE, a shared IP will be used instead.
  • ip4_addr="sge0|" - Assign an interface and IP address.
  • boot=on - Start jail at boot

The properties can be either assigned one by one or all at once during the creation phase They will be explained in the following sections.

To view the Current properties run iocage get all stratus.

Shared ip

VIMAGE allows a virtualized NIC to be used inside of a jail, but it isn’t quite stable. Since having a virtualized interface isn’t a necessity with Nextcloud, I have turned VIMAGE off and used a shared IP instead.

[root]# iocage set vnet=off stratus

In order to use a shared ip, add it with ifconfig.

As the man page mentions, a different netmask then what is in use for the existing IP must be used.

alias   Establish an additional network address for this interface.  This
  is	sometimes useful when changing network numbers,	and one	wishes
  to	accept packets addressed to the	old interface.	If the address
  is	on the same subnet as the first	network	address	for this
  interface,	a non-conflicting netmask must be given.  Usually
  0xffffffff	is most	appropriate.

So if the original network is /24, a netmask of should be used.

For an interface sge0, with a target ip run:

[root]# ifconfig sge0 netmask alias

Don’t forget to add it to rc.conf so that after reboot the IP is re-enabled.

ifconfig_sge0_alias0="inet netmask"

iocage and ZFS

iocage integrates well with ZFS. You may have noticed the command syntax is very similar to the syntax used by ZFS.

It’s possible to ‘jail’ a dataset which gives the jail control of any jailed datasets.

[root]# iocage set jail_zfs=on stratus

Start the jail

With the setup out of the way the jail can be used.

Start the jail:

[root]# iocage start stratus

Tell iocage if you want the jail to start at boot:

[root]# iocage set boot=on stratus

Drop down to the jail’s console:

[root]# iocage console stratus

Inside the jail the process should now be similar to the setup on a regular server.


I have chosen to provide storage to the Nextcloud Jail by mounting a dataset over NFS on my host box. This means my server can focus on serving Nextcloud and my storage box can focus on housing the data. The Nextcloud Jail is not even aware of this since the NFS Mount is simply mounted by the host server into the jail. The other benefit of this is the Nextcloud jail doesn’t need to be able to see my storage server, nor the ability to mount the NFS share itself.

Using a seperate server for storage isn’t neccesary and if the storage for my Nextcloud server was being stored on the same server I would have created a ZFS dataset on the host and mounted it into the jail. I show how to do this in the next section.

NFS Server

On my NFS server I set up a share and noted the permissions.

Parameter Value
Server <nfs server ip>
Share /mnt/tank/data/stratus/data
Owner www:www
UID 80

NFS Client

The next step was to setup an NFS mount on the Nextcloud jail’s host.

Setup NFS

Enable NFS in rc.conf and start the NFS service.

[root]# sysrc nfs_client_enable=YES
[root]# service nfsclient start

Enable locking

[root]# sysrc rpc_lockd_enable=YES rpc_statd_enable=YES
[root]# service lockd start && service statd start

Mount NFS Shares in jail

I’m mounting two shares at /mnt/sync and /mnt/data. /mnt/sync Will be for nextcloud external storage. /mnt/data will be for regular Nextcloud storage.

Note: Make sure the www user, or whichever user Nextcloud is being run by, has permission to access the external storage.

Setup the mountpoints in the jail.

[[email protected]]# mkdir -p /mnt/{data,sync}

Create a script to mount the shares after boot.

[root]# nano /iocage/scripts/stratus/nfsmount.sh


mount ${SERVER}:/mnt/tank/data/stratus/data /iocage/jails/stratus/root/mnt/data
mount ${SERVER}:/mnt/tank/data/syncthing/sync /iocage/jails/stratus/root/mnt/sync

Set it executable.

[root]# chmod +x /iocage/scripts/stratus/nfsmount.sh

And tell iocage to run it post boot.

[root]# iocage set exec_poststart='/iocage/scripts/stratus/nfsmount.sh <nfs server ip>' stratus

Also create one to unmount the shares when the jail is stopped.

[root]# nano /iocage/scripts/stratus/nfsumount.sh

umount /iocage/jails/stratus/root/mnt/data
umount /iocage/jails/stratus/root/mnt/sync

Set it executable.

[root]# chmod +x /iocage/scripts/stratus/nfsumount.sh

And tell iocage to run it post boot.

[root]# iocage set exec_poststop='/iocage/scripts/stratus/nfsumount.sh' stratus


Next I set up a dataset for the database and delegated it into the jail. Using a separate dataset allows me to specify certain properties that are better for a database, it also makes migration easier in case I ever need to move or backup the database.

Make sure the jail has access to the jailed dataset.

[root]# iocage set jail_zfs_dataset=data/jails/stratus/data stratus

Set the mountpoint to none, this way it can just be manipulated in the jail.

[root]# iocage set jail_zfs_mountpoint='none' stratus

Create the dataset that will be used in the jail. I keep my different jail’s datasets separate from iocage organized in vault/data/jails/<jail name> so I put my database in vault/data/jails/stratus/data/db.

[root]# zfs create vault/data/jails/stratus/data/db

Since MySQL uses it’s own cache, it isn’t necessary to cache both metadata and data in the ARC. Set the dataset to only cache metadata.

[root]# zfs set primarycache=metadata vault/data/jails/stratus/data/db

Inside the jail it is now possible to mount the specified dataset normally with ZFS. Mount it to /var/db/mysql

[[email protected]]# zfs set mountpoint=/var/db/mysql vault/data/jails/stratus/data/db

In the jail, check it’s mounted:

[[email protected]]# zfs list

NAME                                                              USED  AVAIL  REFER  MOUNTPOINT
zfs list
NAME                               USED  AVAIL  REFER  MOUNTPOINT
vault                             63.5G   828G    96K  none
vault/data                        4.19G   828G    96K  none
vault/data/jails                  4.19G   828G    96K  none
vault/data/jails/stratus           208M   828G    96K  none
vault/data/jails/stratus/data      207M   828G    88K  none
vault/data/jails/stratus/data/db   207M   828G   108M  /var/db/mysql

Separate Datasets

If you need more data sets they can be created in the jail under the jailed dataset vault/data/jails/stratus/data.

If you wanted to use a separate dataset for your files in nextcloud, and you’re not storing your data on a separate server like the example above of using the NFS server, you would simply create a new dataset.

[[email protected]]# zfs create -o mountpoint=/mnt/files vault/data/jails/stratus/data/files
[[email protected]]# zfs list

NAME                                  USED  AVAIL  REFER  MOUNTPOINT
vault                                63.5G   828G    96K  none
vault/data                           4.19G   828G    96K  none
vault/data/jails                     4.19G   828G    96K  none
vault/data/jails/stratus              208M   828G    96K  none
vault/data/jails/stratus/data         207M   828G    88K  none
vault/data/jails/stratus/data/db      207M   828G   108M  /var/db/mysql
vault/data/jails/stratus/data/files    88K   828G    88K  /mnt/files


With most of the requirements in place it was time to start setting up Nextcloud. The requirements for Nextcloud include your basic web stack of a web server, database, and PHP.

The recommended setup for Nextcloud is:

  • MySQL/MariaDB
  • PHP 7.0 +
  • Apache 2.4 with mod_php

While my preferred setup would be PostgreSQL, and Nginx, the majority of the documentation is based on the recommended setup. It would be nice to see these options better supported in the future. As of now there is no ‘official’ documentation for Nginx.

Pre Configuration Setup

Install Apache and enable it to start on boot.

[[email protected]]# pkg install apache24
[[email protected]]# sysrc apache24_enable=YES
[[email protected]]# service apache24 start

Navigate to the jail’s ip and, “It Works!” should be displayed.

TLS Certificate

With Apache up and running, I configured https using let’s encrypt.

In order to properly automate the renewal of certificates I set up an ‘acme’ user which renews the certificate on a cron job using acme.sh.

[[email protected]]# adduser

Username   : acme
Password   : <disabled>
Full Name  : Lets Encrypt acme.sh Client
Uid        : 1007
Class      :
Groups     : acme
Home       : /home/acme
Home Mode  :
Shell      : /bin/sh
Locked     : no

Add the ‘acme’ user to a ‘certs’ group.

[[email protected]]# pw groupadd certs
[[email protected]]# pw groupmod certs -m acme
Lets Encrypt Client

Install acme.sh to the acme user’s home directory.

[[email protected]]# su acme
[[email protected]]$ cd ~
[[email protected]]$ curl -O https://raw.githubusercontent.com/Neilpang/acme.sh/master/acme.sh
[[email protected]]$ chmod +x acme.sh
[[email protected]]$ ./acme.sh --install

Alternate configuration is described in the documentation but by default this will install to ~/.acme.sh and do the following.

[Wed May 10 03:10:42 UTC 2017] Installing to /home/acme/.acme.sh
[Wed May 10 03:10:42 UTC 2017] Installed to /home/acme/.acme.sh/acme.sh
[Wed May 10 03:10:42 UTC 2017] Installing alias to '/home/acme/.profile'
[Wed May 10 03:10:42 UTC 2017] OK, Close and reopen your terminal to start using acme.sh
[Wed May 10 03:10:42 UTC 2017] Installing alias to '/home/acme/.cshrc'
[Wed May 10 03:10:42 UTC 2017] Installing cron job
crontab: no crontab for acme
crontab: no crontab for acme
[Wed May 10 03:10:43 UTC 2017] OK
Confgure Challenge

The default way to acquire a certificate involves creating a challenge in the webroot of a server.

This can be done by specifying the webroot and domain to issue a certificate for.

[[email protected]]$ acme.sh  --issue  -d <domain> -w <webroot>

At the time of setting up my server, it was not exposed to the internet. I had recently configured certificates for unexposed services using dns validation. Due to my familiarity, and the convenience of not needing to have a server accessible from the web, I issued my challenge this way.

To use DNS validation I used cloudflare - which along with a few other DNS providers has an api which supports a DNS challenge. I found my API key on my web dashboard.

I set the necesary environment variables.

[[email protected]]$ export CF_Email="<cloudflare email>"
[[email protected]]$ export CF_Key="<cloudflare api key>"

And issued a certificate for my domain.

[[email protected]]$ /usr/home/acme/.acme.sh/acme.sh --issue --dns dns_cf -d <domain name>

Letsencrypt should give a TXT record to add to your DNS records. Once the record has been added, run renew and your certificates should be issued.

I use <domain name> In the place of my domain for the remainder of this article.

Along with issuing a certificate, this wrote the required configuration file for future certificates to ~/.acme.sh/<domain name>/<domain name>.conf.

[[email protected]]$ /usr/home/acme/.acme.sh/acme.sh --renew -d <domain name>

In order to give my acme user access to the location it issues the certificate to, Earlier I created a ‘certs’ group and added the ‘acme’ user, add the ‘www’ user.

[[email protected]]# pw groupmod certs -M acme,www

Set the proper permissions in the directory the certificate will be located in.

[[email protected]]# chown -R www:certs /usr/local/etc/apache24/ssl
[[email protected]]# chmod -R 770 /usr/local/etc/apache24/ssl

After creating a new certificate, the web server should be reloaded. To give the acme user permission to reload the server, I used visudo.

Edit visudo to allow a reload without a password.:

[[email protected]]# pkg install sudo
[[email protected]]# visudo

acme ALL=(ALL) NOPASSWD: /usr/sbin/service apache24 reload

This will allow the user to run the following in a cron job:

[[email protected]]# sudo /usr/sbin/service apache24 reload
Cron Job

As the ‘acme’ user, add the reload command to the configuration file we used earlier, ~/.acme.sh/<domain name>/<domain name>.conf.

Le_ReloadCmd='/usr/local/bin/sudo /usr/sbin/service apache24 reload'

Now add the crontab that will periodically renew the certificate.

[[email protected]]# crontab -u acme -e

36 0 * * * "/usr/home/acme/.acme.sh"/acme.sh --cron --home "/usr/home/acme/.acme.sh"

To set the location the different certificate files will be deployed to, tell ‘acme.sh’ where they should end up.

[[email protected]]$ /usr/home/acme/.acme.sh/acme.sh --installcert -d <domain name> \
  --certpath /usr/local/etc/apache24/ssl/<domain name>.cer \
  --keypath /usr/local/etc/apache24/ssl/<domain name>.key \
  --capath /usr/local/etc/apache24/ssl/ca.cer \
  --fullchainpath /usr/local/etc/apache24/ssl/fullchain.cer

This will write the configuration to ~/.acme.sh/<domain name>/<domain name>.conf.

After obtaining the cert, the following PEM-encoded files will be located in /usr/local/etc/apache24/ssl/.

[[email protected]]$ ls -la /usr/local/etc/apache24/ssl/

total 35
drwxrwx---  2 www   certs     6 May 31 02:45 .
drwxr-xr-x  7 root  wheel    13 May 31 02:15 ..
-rw-r--r--  1 acme  certs  1647 May 31 02:45 ca.cer
-rw-r--r--  1 acme  certs  3465 May 31 02:45 fullchain.cer
-rw-r--r--  1 acme  certs  1818 May 31 02:45 stratus.ramsden.network.cer
-rw-r--r--  1 acme  certs  1675 May 31 02:45 stratus.ramsden.network.key
<domain name>.cer The domain certificate
ca.cer The Let’s Encrypt chain certificate
fullchain.cer cert.pem and chain.pem combined
<domain name>.key The certificate’s private key


Next to set up the database. Install Mariadb and set it to start on boot.

[[email protected]]# pkg install mariadb101-server
[[email protected]]# sysrc mysql_enable=YES

The different configuration options can be found in /usr/local/share/mysql/

[[email protected]]# ls -l /usr/local/share/mysql/my*.cnf

Use the appropriate configuration file for the size of database. I used the small configuration. Copy it to /usr/local/etc/my.cnf.

[[email protected]]# cp /usr/local/share/mysql/my-small.cnf /usr/local/etc/my.cnf

Set maximum packet size to 32M in /usr/local/etc/my.cnf

  • max_allowed_packet = 32M

Start MySQL and run the script to secure the configuration.

[[email protected]]# service mysql-server start && /usr/local/bin/mysql_secure_installation
Prepare Database

Login to the database.

[[email protected]]# mysql -u root -p

Create user nextcloud_admin and add a password.


CREATE USER 'nextcloud_admin'@'localhost' IDENTIFIED BY '<your password here>';

GRANT ALL ON nextcloud.* TO 'nextcloud_admin'@'localhost';



With the database configured, it can now be restarted.

[[email protected]]# service mysql-server restart


Install PHP and all modules you require. I’m using PHP 7.1. Nextcloud recommends using a version 7.0 or newer.

[[email protected]]# pkg install php71 mod_php71

You can check the compiled in modules with: php -r “phpinfo();”. The following packages are required:

  • php71-ctype
  • php71-dom
  • php71-gd
  • php71-iconv
  • php71-json
  • php71-mbstring
  • php71-posix
  • php71-simplexml
  • php71-xmlreader
  • php71-xmlwriter
  • php71-zip
  • php71-zlib
  • php71-pdo_mysql

These were not listed as required by Nextcloud, but my setup wouldn’t work without them.

  • php71-hash
  • php71-xml
  • php71-session
  • php71-mysqli
  • php71-filter
  • php71-xsl
  • php71-wddx

Recommended packages:

  • php71-curl
  • php71-fileinfo
  • php71-bz2
  • php71-intl
  • php71-mcrypt
  • php71-openssl

Required for specific apps:

  • php71-ldap
  • php71-ftp
  • php71-imap

Required for specific apps: (optional):

  • php71-exif
  • php71-gmp

For enhanced server performance (optional). I used redis. The options are:

  • pecl-APCu (broken on FreeBSD at the time of writing)
  • php71-memcache
  • pecl-redis
  • php71-opcache

For preview generation (optional):

  • pecl-imagick
  • ffmpeg
  • libreoffice

For command line processing (optional):

  • php71-pcntl
Install Required Modules

I installed the following.

[[email protected]]# pkg install php71-ctype php71-dom php71-gd php71-iconv php71-json php71-mbstring php71-posix php71-simplexml  php71-xmlreader php71-xmlwriter php71-zip php71-zlib php71-pdo_mysql php71-curl php71-fileinfo php71-bz2 php71-intl php71-mcrypt php71-openssl php71-exif pecl-redis pecl-imagick php71-pcntl ffmpeg libreoffice php71-hash php71-xml php71-session php71-mysqli php71-wddx php71-xsl php71-filter php71-opcache

Configure Web Server

Configure Apache and PHP and confirm they are working properly.


Uncomment rewrite_module and ssl_module in /usr/local/etc/apache24/httpd.conf.

Add the location for the certificate we configured earlier.

SSLCertificateFile /usr/local/etc/apache24/ssl/fullchain.cer
SSLCertificateKeyFile /usr/local/etc/apache24/ssl/<domain name>.key

Make sure the ssl and rewrite modules are uncommented.

LoadModule ssl_module libexec/apache24/mod_ssl.so
LoadModule rewrite_module libexec/apache24/mod_rewrite.so

Make sure the php7 module is uncommented.

LoadModule php7_module        libexec/apache24/libphp7.so

After libphp7.so line add

<IfModule php7_module>
   <FilesMatch "\.(php|phps|php7|phtml)$">
       SetHandler php7-script
   DirectoryIndex index.php

Inside the IFModule mime_module block add:

AddType application/x-httpd-php-source .phps
AddType application/x-httpd-php        .php

Add a PHP handler, in /usr/local/etc/apache24/modules.d/001_mod_php.conf.

<FilesMatch "\.php$">
    SetHandler application/x-httpd-php
<FilesMatch "\.phps$">
    SetHandler application/x-httpd-php-source

Restart apache:

[[email protected]]# service apache24 restart


Grab php.ini

[[email protected]]# cp /usr/local/etc/php.ini-production /usr/local/etc/php.ini && rehash

I used the following configuration in /usr/local/etc/php.ini:

  • cgi.fix_pathinfo=0 - Change cgi.fix_pathinfo=1 to cgi.fix_pathinfo=0
  • date.timezone = UTC - Set date.timezone to your timezone zone, or UTC.
  • post_max_size = 10240M - This is the maximum size of POST data accepted by PHP. Setting it to zero removes the limit.
  • upload_max_filesize = 10240M - Maximum allowed size for uploaded files.
  • memory_limit = 512M - Adjust memory limit, change this based on your server.

It’s also recommended to enable opcache in /usr/local/etc/php.ini, which caches precompiled bytecode. It’s bundled in with PHP after version 5.5.

Nextcloud recommends these settings:

  • 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
Test PHP

With PHP configured we can restart the Web server and check if PHP is working properly.

Restart apache

[[email protected]]# service apache24 restart

Check if everything is working by checking phpinfo(). Enable it by adding the following to /usr/local/www/apache24/data/info.php


Check your domain or ip address in a browser http://<ip address>/info.php. All of the PHP info should be listed there.


It’s recommended to use some sort of database cache, I used redis.

Install redis and set it to start at boot.

[[email protected]]# pkg install redis
[[email protected]]# sysrc redis_enable=YES

Note: in order to use pecl-redis with php71, pecl-redis needs to be built with php71. It defaults to using php56 which is how it is built in the package. I built a package using poudriere with my required compile options.

Next we will configure redis in /usr/local/etc/redis.conf

Change the port to 0 so Redis will not listen on a TCP socket.

port 0

Instead we will configure a UNIX socket.

unixsocket /tmp/redis.sock
unixsocketperm 777

Redis can now be started.

[[email protected]]# service redis start

Run ls -al /tmp you should see redis.sock and mysql.sock in the list.

[[email protected]]# ls -al /tmp

total 43
srwxrwxrwx   1 mysql  wheel    0 May 31 09:29 mysql.sock
srwxrwxrwx   1 redis  wheel    0 May 31 09:30 redis.sock

Configure Nextcloud

Finally we can install nextcloud. Download it and check it’s hash.

[[email protected]]# cd /usr/local/www/apache24/data/
[[email protected]]# fetch https://download.nextcloud.com/server/releases/nextcloud-12.0.0.tar.bz2
[[email protected]]# fetch https://download.nextcloud.com/server/releases/nextcloud-12.0.0.tar.bz2.md5

[[email protected]]# md5 nextcloud-12.0.0.tar.bz2
[[email protected]]# cat nextcloud-12.0.0.tar.bz2.md5

If the hash matches, the archive can be extracted.

[[email protected]]# tar -xjf nextcloud-12.0.0.tar.bz2

Nextcloud Server Configuration

Create a file for nextcloud under /usr/local/etc/apache24/Includes/. I put mine in /usr/local/etc/apache24/Includes/<domain name>.conf

If you set https earlier you can use the following virtual host. I used HSTS here which means the server will always expect to use an https certificate, and removing it in the future will be difficult. If you are considering removing one at a later date, make sure you know how HSTS works.

Add the following virtual host replacing the ServerAdmin email, and ServerName domain name.

<VirtualHost *:443>
    ServerAdmin admin@<domain name>
    ServerName <domain name>
    DirectoryIndex index.php
    DocumentRoot /usr/local/www/apache24/data/nextcloud

    SSLCertificateFile /usr/local/etc/apache24/ssl/fullchain.cer
    SSLCertificateKeyFile /usr/local/etc/apache24/ssl/<domain name>.key
    SSLEngine on
    # Intermediate configuration, tweak to your needs
    SSLProtocol             all -SSLv2 -SSLv3
    SSLHonorCipherOrder     on
    SSLCompression          off
    SSLOptions +StrictRequire

    <Directory /usr/local/www/apache24/data/nextcloud>
      AllowOverride all

    <IfModule mod_headers.c>
      Header always set Strict-Transport-Security "max-age=15552000; includeSubDomains"

If you didn’t set up https your virtual host would look something like this.

<VirtualHost *:80>
  DocumentRoot "/usr/local/www/apache24/data/nextcloud"
  ServerName <domain name>
     RewriteEngine on
     RewriteCond %{SERVER_NAME} =<domain name>
     RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,QSA,R=permanent]
  #ErrorLog ${APACHE_LOG_DIR}/error.log
  #CustomLog ${APACHE_LOG_DIR}/access.log combined

  <Directory /usr/local/www/apache24/data/nextcloud/>
    Options +FollowSymlinks
    AllowOverride All
    <IfModule mod_dav.c>
      Dav off
    SetEnv HOME /usr/local/www/apache24/data/nextcloud
    SetEnv HTTP_HOME /usr/local/www/apache24/data/nextcloud
    Satisfy Any

If you setup https, find ‘Listen 80’ and add underneath ‘Listen 443’ in/usr/local/etc/apache24/httpd.conf.

Ask search engine bots not to index your site

[[email protected]]# ln -s /usr/local/www/apache24/data/nextcloud/robots.txt /usr/local/www

Make sure the permissions are still correct.

[[email protected]]# chown -R www:www /usr/local/www/apache24/data/nextcloud /mnt/data

Set the permissions more securely.

[[email protected]]# find /usr/local/www/apache24/data/nextcloud/ -type d -exec chmod 750 {} \;
[[email protected]]# find /usr/local/www/apache24/data/nextcloud/ -type f -exec chmod 640 {} \;


Nextcloud should now be working, restart the webserver, visit your domain and edit the settings.

[[email protected]]# service apache24 restart

Filling the different options with the locations and values we used earlier. The password should be the one that was used when setting up the database.

Option Value
Data folder /mnt/data
Database user nextcloud_admin
Database password <your password>
Database name nextcloud
Database host localhost:/tmp/mysql.sock


Enable the Nextcloud crontab which runs periodic tasks.

[[email protected]]# crontab -u www -e

*/15 * * * * /usr/local/bin/php -f /usr/local/www/apache24/data/nextcloud/cron.php

Verify it’s scheduled with:

[[email protected]]# crontab -u www -l

Nextcloud Providers

All sorts of Nextcloud commands can be set on the command line using the occ tool. These are the providers I set. It is fairly self-explanatory what they do.

[[email protected]]# su -m www -c 'php /usr/local/www/apache24/data/nextcloud/occ config:system:set enable_previews --value=true --type=boolean'
[[email protected]]# su -m www -c 'php /usr/local/www/apache24/data/nextcloud/occ config:system:set enabledPreviewProviders 0 --value="OC\Preview\PNG"'
[[email protected]]# su -m www -c 'php /usr/local/www/apache24/data/nextcloud/occ config:system:set enabledPreviewProviders 1 --value="OC\Preview\JPEG"'
[[email protected]]# su -m www -c 'php /usr/local/www/apache24/data/nextcloud/occ config:system:set enabledPreviewProviders 2 --value="OC\Preview\GIF"'
[[email protected]]# su -m www -c 'php /usr/local/www/apache24/data/nextcloud/occ config:system:set enabledPreviewProviders 3 --value="OC\Preview\BMP"'
[[email protected]]# su -m www -c 'php /usr/local/www/apache24/data/nextcloud/occ config:system:set enabledPreviewProviders 4 --value="OC\Preview\XBitmap"'
[[email protected]]# su -m www -c 'php /usr/local/www/apache24/data/nextcloud/occ config:system:set enabledPreviewProviders 5 --value="OC\Preview\MarkDown"'
[[email protected]]# su -m www -c 'php /usr/local/www/apache24/data/nextcloud/occ config:system:set enabledPreviewProviders 6 --value="OC\Preview\MP3"'
[[email protected]]# su -m www -c 'php /usr/local/www/apache24/data/nextcloud/occ config:system:set enabledPreviewProviders 7 --value="OC\Preview\TXT"'
[[email protected]]# su -m www -c 'php /usr/local/www/apache24/data/nextcloud/occ config:system:set enabledPreviewProviders 8 --value="OC\Preview\Illustrator"'
[[email protected]]# su -m www -c 'php /usr/local/www/apache24/data/nextcloud/occ config:system:set enabledPreviewProviders 9 --value="OC\Preview\Movie"'
[[email protected]]# su -m www -c 'php /usr/local/www/apache24/data/nextcloud/occ config:system:set enabledPreviewProviders 10 --value="OC\Preview\MSOffice2003"'
[[email protected]]# su -m www -c 'php /usr/local/www/apache24/data/nextcloud/occ config:system:set enabledPreviewProviders 11 --value="OC\Preview\MSOffice2007"'
[[email protected]]# su -m www -c 'php /usr/local/www/apache24/data/nextcloud/occ config:system:set enabledPreviewProviders 12 --value="OC\Preview\MSOfficeDoc"'
[[email protected]]# su -m www -c 'php /usr/local/www/apache24/data/nextcloud/occ config:system:set enabledPreviewProviders 13 --value="OC\Preview\OpenDocument"'
[[email protected]]# su -m www -c 'php /usr/local/www/apache24/data/nextcloud/occ config:system:set enabledPreviewProviders 14 --value="OC\Preview\PDF"'
[[email protected]]# su -m www -c 'php /usr/local/www/apache24/data/nextcloud/occ config:system:set enabledPreviewProviders 15 --value="OC\Preview\Photoshop"'
[[email protected]]# su -m www -c 'php /usr/local/www/apache24/data/nextcloud/occ config:system:set enabledPreviewProviders 16 --value="OC\Preview\Postscript"'
[[email protected]]# su -m www -c 'php /usr/local/www/apache24/data/nextcloud/occ config:system:set enabledPreviewProviders 17 --value="OC\Preview\StarOffice"'
[[email protected]]# su -m www -c 'php /usr/local/www/apache24/data/nextcloud/occ config:system:set enabledPreviewProviders 18 --value="OC\Preview\SVG"'
[[email protected]]# su -m www -c 'php /usr/local/www/apache24/data/nextcloud/occ config:system:set enabledPreviewProviders 19 --value="OC\Preview\TIFF"'
[[email protected]]# su -m www -c 'php /usr/local/www/apache24/data/nextcloud/occ config:system:set enabledPreviewProviders 20 --value="OC\Preview\Font"'


[[email protected]]# su -m www -c 'php /usr/local/www/apache24/data/nextcloud/occ config:system:set memcache.local --value="\OC\Memcache\Redis"'
[[email protected]]# su -m www -c 'php /usr/local/www/apache24/data/nextcloud/occ config:system:set memcache.locking --value="\OC\Memcache\Redis"'
[[email protected]]# su -m www -c 'php /usr/local/www/apache24/data/nextcloud/occ config:system:set redis host --value="/tmp/redis.sock"'
[[email protected]]# su -m www -c 'php /usr/local/www/apache24/data/nextcloud/occ config:system:set redis port --value=0 --type=integer'

Post Setup

That should be the end of the set up. Some things you might want to do now are look through the administrator settings and enable some defaults depending on your use case. You’ll probably also want to create some users and setup email so that the Nextcloud server can contact you. I recommend using SMTP for simplicity.

Nextcloud file browser.
Nextcloud file browser.

Manual Upgrade

I’ve had problems with the WebUI updater and it has always failed on me. I do the upgrades manually from the commandline, but it doesn’t hurt to try the WebUI updater since it can be completed from the commandline if it fails.


Stop Apache and enter maintenance mode

[[email protected]]# service apache24 stop
[[email protected]]# su -m www -c 'php /usr/local/www/apache24/data/nextcloud/occ maintenance:mode --on'

Create a backup directory and enter it.

[[email protected]]# mkdir -p /mnt/ncbup && cd /mnt/ncbup

Backup Database and Nextcloud Directory

Just to be safe, dump the database.

[[email protected]]# mysqldump --lock-tables -h localhost -u root -p nextcloud > nextcloud-sqlbkp_`date +"%Y%m%d"`.bak

Make a backup of the old Nextcloud directory. This contains the old configuration, so for now it must be held onto.

[[email protected]]# mv /usr/local/www/apache24/data/nextcloud /mnt/ncbup/nextcloud-old-`date +"%Y%m%d"`.bak

Prepare Upgrade

Download source.

[[email protected]]# cd /mnt/ncbup
[[email protected]]# fetch https://download.nextcloud.com/server/releases/nextcloud-12.0.2.tar.bz2
[[email protected]]# fetch https://download.nextcloud.com/server/releases/nextcloud-12.0.2.tar.bz2.md5

check hash of files, they should be the same.

[[email protected]]# md5 nextcloud-12.0.2.tar.bz2
[[email protected]]# cat nextcloud-12.0.2.tar.bz2.md5

If good, extract

[[email protected]]# tar -xjf nextcloud-12.0.2.tar.bz2

Move updated files to nextcloud location.

[[email protected]]# mv nextcloud /usr/local/www/apache24/data/
[[email protected]]# chown -R www:www /usr/local/www/apache24/data/nextcloud

Copy the config.php file from your old Nextcloud directory to your new Nextcloud directory.

[[email protected]]# cp /mnt/ncbup/nextcloud-old-`date +"%Y%m%d"`.bak/config/config.php /usr/local/www/apache24/data/nextcloud/config/config.php

Compare old apps with new ones. If any are missing from the new directory, copy them over from the original.

[[email protected]]# ls /mnt/ncbup/nextcloud-old-`date +"%Y%m%d"`.bak/apps/
[root@stratus]# ls /usr/local/www/apache24/data/nextcloud/apps/

I needed to move:

  • bruteforcesettings
  • files_markdown
  • twofactor_totp
[root@stratus]# cd /mnt/ncbup/nextcloud-old-`date +"%Y%m%d"`.bak/apps
[root@stratus]# cp -r bruteforcesettings files_markdown twofactor_totp /usr/local/www/apache24/data/nextcloud/apps/

Make sure the permissions on the new directory are correct.

[root@stratus]# chown -R www:www /usr/local/www/apache24/data/nextcloud
[root@stratus]# find /usr/local/www/apache24/data/nextcloud/ -type d -exec chmod 750 {} \;
[root@stratus]# find /usr/local/www/apache24/data/nextcloud/ -type f -exec chmod 640 {} \;


Start the upgrade.

[root@stratus]# su -m www -c 'php /usr/local/www/apache24/data/nextcloud/occ upgrade'

Assuming the upgrade succeeded, disable the maintenance mode and start apache.

[root@stratus]# ssu -m www -c 'php /usr/local/www/apache24/data/nextcloud/occ maintenance:mode --off'
[root@stratus]# service apache24 start

Archive Backup

It might be a good idea to hold on to the files in case there are ever needed. I archive my different backups into a tarball.

Create a directory with all the upgrade files.

[root@stratus]# mkdir archive
[root@stratus]# tar -cvzf archive/12.0.0-12.0.2-upgrade-`date +"%Y%m%d"`.tar.gz nextcloud-12.0.2.tar.bz2 nextcloud-old-20170818.bak nextcloud-12.0.2.tar.bz2.md5 nextcloud-sqlbkp_20170818.bak

Now the old files can be removed.

[root@stratus]# rm -rf nextcloud-12.0.2.tar.bz2 nextcloud-old-20170818.bak nextcloud-12.0.2.tar.bz2.md5 nextcloud-sqlbkp_20170818.bak

You probably also want to take ZFS snapshots of your jail and dataset, as it needs for easy rollback and transfer of data.


Updated on August, 18th, 2017