Hello everyone and welcome. Although this tutorial was written for Nextcloud 20 on centos stream 8, it is still working for Nextcloud 24. This tutorial will cover setting up php 7.4, an SSL from Letsencrypt, Redis for caching, and also cover all the recommended server tuning for Nextcloud. Read on and learn how to install nextcloud. This is a complete guide.

This guide assumes that you own a domain, have a DNS record pointed to the location of your server, and have ports forwarded through your firewall for accessing remotely. This guide also assumes you are working on a fresh install of CentOS and do not have php, nginx, etc… installed.

Let’s Begin!


Initial Prep:

The first thing we are going to want to do is download and install everything required for our setup. I would suggest using ‘su’ to root since we are going to be using root the entire time, and we don’t want to type sudo before every command. I would also recommend logging into the server with ssh, so you can copy and paste these commands (unless you have a gui on your server you can copy paste into the terminal).

Add the epel repository.

# dnf install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm

Add the Remi repo for php 7.4.

# dnf install -y https://rpms.remirepo.net/enterprise/remi-release-8.rpm
# dnf install -y dnf-utils

Now enable the remi repo.

# dnf module enable php:remi-7.4

Install the LEMP stack.

# dnf install -y nginx

Now enable nginx.

# systemctl enable --now nginx

Install MariaDB.

# dnf install -y mariadb-server

Enable MariaDB.

systemctl enable --now mariadb

Install redis.

# dnf install redis

Enable Redis.

# systemctl enable --now redis

Install php and required modules.

# dnf install -y php php-fpm php-mysqlnd
# dnf install -y php-gd php-json php-curl php-mbstring php-intl php-xml php-zip php-pear php-soap php-bcmath php-gmp php-opcache php-imagick php-pecl-redis php-pecl-apcu

Enable php.

# systemctl enable --now php-fpm

Install Certbot.

# dnf install certbot python3-certbot-nginx

Configuration:

Now that we have everything we need, we need to do some configuration before we can continue.

Open the following ports in the firewall

# firewall-cmd --add-port={80,443}/tcp --permanent

Reload the firewall

# firewall-cmd --reload

Perform the initial installation of MariaDB

# mysql_secure_installation

The first thing it will have you do is set the ROOT password for mariadb.

After it will ask you to disallow anonymous users, disallow remote root login, remove the test database and access to it, and reload the privilege tables.

Select ‘y’ on all the aforementioned questions.

Now let’s configure the database.

# mysql -u root -p

Login with the root password you just set.

Now create the database. Items in bold can be changed to your preference.

MariaDB [(none)]> create database ncdb;
MariaDB [(none)]> grant all privileges on ncdb.* to username@localhost identified by 'password';
MariaDB [(none)]> flush privileges;
MariaDB [(none)]> quit

Now lets tune php.

Use whatever text editor you would like, I am going to use nano.

First edit the php.ini.

# nano /etc/php.ini

Change the value of/uncomment (by removing the ‘;’) on the following lines.

cgi.fix_pathinfo=0
memory_limit=512M

Save and exit the file.

Now let’s modify our www.conf file.

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

Change the settings to match these.

user = nginx
group = nginx

Uncomment these lines by removing the ‘;’.


env[HOSTNAME] = $HOSTNAME
env[PATH] = /usr/local/bin:/usr/bin:/bin
env[TMP] = /tmp
env[TMPDIR] = /tmp
env[TEMP] = /tmp

And finally change these lines.

pm.max_children = 120
pm.start_servers = 12
pm.min_spare_servers = 6
pm.max_spare_servers = 18

Save and exit the file.

Now let’s configure the opcache.

# nano /etc/php.d/10-opcache.ini

Uncomment (by removing the ‘;’) and adjust values of the following lines.

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

Get a certificate from certbot.

# certbot certonly --nginx

Answer the questions: Add your email for renewel notifications, agree to the terms of service, decide to share your email address, and enter your domain name when asked.

Once it completes it will look something like this:

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/your.domain.com/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/your.domain.com/privkey.pem
   Your cert will expire on 2021-03-08. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot
   again. To non-interactively renew *all* of your certificates, run
   "certbot renew"

Make sure to remember the location of your cert and key as we will need this later

/etc/letsencrypt/live/your.domain.com/fullchain.pem
/etc/letsencrypt/live/your.domain.com/privkey.pem

Install Nextcloud:

Now we can finally install nextcloud.

Download the latest version.

# wget https://download.nextcloud.com/server/releases/latest.zip

Extract into your webroot.

# unzip latest.zip -d /usr/share/nginx/html/

Now chown to make nginx the owner

# chown -R nginx:nginx /usr/share/nginx/html/nextcloud

Configure nginx for nextcloud.

# nano /etc/nginx/conf.d/nextcloud.conf

Copy and paste the following info into the conf file.

Items in bold should be changed to your specific settings!

upstream php-handler {
    server unix:/run/php-fpm/www.sock;
}

server {
    listen 80;
    server_name your.domain.com;
    # enforce https
    return 301 https://$server_name:443$request_uri;
}

server {
    listen 443 ssl http2;
    server_name your.domain.com;

    ssl_certificate /etc/letsencrypt/live/your.domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/your.domain.com/privkey.pem;

    add_header Strict-Transport-Security “max-age=15552000" always;
    add_header Referrer-Policy "no-referrer" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Download-Options "noopen" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Permitted-Cross-Domain-Policies "none" always;
    add_header X-Robots-Tag "none" always;
    add_header X-XSS-Protection "1; mode=block" always;
    fastcgi_hide_header X-Powered-By;

    # Path to the root of your installation
    root /usr/share/nginx/html/nextcloud;

    access_log /var/log/nginx/nc_access_log;
    error_log /var/log/nginx/nc_error_log;

    location = /robots.txt {
        allow all;
        log_not_found off;
        access_log off;
    }

    rewrite ^/.well-known/webfinger /nextcloud/public.php?service=webfinger last;
    rewrite ^/.well-known/nodeinfo /nextcloud/public.php?service=nodeinfo last;
    location = /.well-known/carddav {
      return 301 $scheme://$host:$server_port/remote.php/dav;
    }
    location = /.well-known/caldav {
      return 301 $scheme://$host:$server_port/remote.php/dav;
    }

    # set max upload size
    client_max_body_size 512M;
    fastcgi_buffers 64 4K;

    # Enable gzip but do not remove ETag headers
    gzip on;
    gzip_vary on;
    gzip_comp_level 4;
    gzip_min_length 256;
    gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
    gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;

    location / {
        rewrite ^ /index.php;
    }

    location ~ ^\/(?:build|tests|config|lib|3rdparty|templates|data)\/ {
        deny all;
    }
    location ~ ^\/(?:\.|autotest|occ|issue|indie|db_|console) {
        deny all;
    }

    location ~ ^\/(?:index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|oc[ms]-provider\/.+)\.php(?:$|\/) {
        fastcgi_split_path_info ^(.+?\.php)(\/.*|)$;
        set $path_info $fastcgi_path_info;
        try_files $fastcgi_script_name =404;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $path_info;
        fastcgi_param HTTPS on;
        fastcgi_param modHeadersAvailable true;
        fastcgi_param front_controller_active true;
        fastcgi_pass php-handler;
        fastcgi_intercept_errors on;
        fastcgi_request_buffering off;
    }

    location ~ ^\/(?:updater|oc[ms]-provider)(?:$|\/) {
        try_files $uri/ =404;
        index index.php;
    }

    location ~ \.(?:css|js|woff2?|svg|gif|map)$ {
        try_files $uri /index.php$request_uri;
        add_header Cache-Control "public, max-age=15778463";
        add_header Referrer-Policy "no-referrer" always;
        add_header X-Content-Type-Options "nosniff" always;
        add_header X-Download-Options "noopen" always;
        add_header X-Frame-Options "SAMEORIGIN" always;
        add_header X-Permitted-Cross-Domain-Policies "none" always;
        add_header X-Robots-Tag "none" always;
        add_header X-XSS-Protection "1; mode=block" always;

        access_log off;
    }

    location ~ \.(?:png|html|ttf|ico|jpg|jpeg|bcmap)$ {
        try_files $uri /index.php$request_uri;
        access_log off;
    }
}

Save and quit the file.

Create a data directory outside of the webroot. Not needed if you are going to use the default data location which is inside the webroot /nextcloud/data/. I would recommend keeping the data directory OUT of the webroot.

# mkdir /ncdata

Chown this folder to be owned by nginx and set permissions.

# chown nginx:nginx /ncdata
# chmod 750 /ncdata

Set permissions for the nextcloud installation.

# find /usr/share/nginx/html/nextcloud/ -type d -exec chmod 750 {} \;
# find /usr/share/nginx/html/nextcloud/ -type f -exec chmod 640 {} \;

Set php ownership to nginx.

# chown nginx:nginx -R /var/lib/php/session/

Now verify nginx for syntax errors.

# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Configure SELinux.

# semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/nginx/html/nextcloud/data(/.*)?'
# semanage fcontext -a -t httpd_sys_rw_content_t '/ncdata(/.*)?'
# semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/nginx/html/nextcloud/config(/.*)?'
# semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/nginx/html/nextcloud/apps(/.*)?'
# semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/nginx/html/nextcloud/.htaccess'
# semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/nginx/html/nextcloud/.user.ini'
# semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/nginx/html/nextcloud/3rdparty/aws/aws-sdk-php/src/data/logs(/.*)?'

# restorecon -Rv '/usr/share/nginx/html/nextcloud/'
# restorecon -Rv '/ncdata/'

Create a cron job for nginx to call cron.php every 5 minutes.

# crontab -u nginx -e

Place the following command in the cron file

*/5 * * * * php -f /usr/share/nginx/html/nextcloud/cron.php

Save and quit. You can verify the job has been added with the following command.

# crontab -u nginx -l
*/5 * * * * php -f /usr/share/nginx/html/nextcloud/cron.php


OPTIONAL:

Disable SELinux or set to permissive mode if it gives you issues about writing to the data directory during initial setup.

# setenforce permissive
# nano /etc/selinux/config

Change the following line to disabled or permissive.

SELINUX=permissive

Now reload nginx and php.

# systemctl restart nginx php-fpm

Finalize nextcloud install:

Open a browser and point it to your nextcloud install. If you do not have DNS or NAT relfection configured in your firewall you can access the instance from its ip address.

Type either the domain name or the IP of the server into the address bar of your browser.

Fill out the username and password blocks, select “mysql” in the next section and fill the database user, password, and name to everything that you configured earlier. Then click “Finish Setup”.

*NOTE*

I’m not sure if it is a bug or not, but sometimes it will timeout with a 504 error, and when you refresh you are at the same page again. Entering the same username and password will cause it to say that user already exists. To get around this reload php and nginx again.

# systemctl restart nginx php-fpm

And then it should bring you to a login page only, where you can enter your credentials.

If all goes well you will be presented with the dashboard screen!

A few more things to finish

Add trusted domains to the config.

Edit the config.php in nextcloud.

nano /usr/share/nginx/html/nextcloud/config/config.php

Add your trusted domain into the configuration under ‘trusted domains’

'trusted_domains' => 
  array (
    0 => 'your.domain.com',
    1 => 'IP address of server',
  ),

Add nginx to the redis group

# usermod -a -G nginx redis

At the bottom of the file add these lines to configure nextcloud to use redis.

Add the lines after the ‘installed’ => true,’ line.

  'memcache.locking' => '\\OC\\Memcache\\Redis',
  'memcache.distributed' => '\\OC\\Memcache\\Redis',
  'memcache.local' => '\\OC\\Memcache\\Redis',
  'redis' => 
  array (
    'host' => 'localhost',
    'port' => 6379,
    'timeout' => 3,
  ),

Set your default phone region in the config file by adding these lines before the end of the file.

'default_phone_region' => 'US',

Configure SELinux for this change.

# setsebool -P httpd_can_network_connect 1

And finally, restart nginx and php-fpm again.

# systemctl restart nginx php-fpm

Refresh your browser window and you are done.


Conclusion

This guide should of got you up and running with nextcloud. This is a complete install that will keep the security and setup recommendations tab of settings from nagging you about server tuning. If you have any questions or would like to help improve this guide, leave a comment or contact me through the contact page web form.

Thank you and Happy Clouding!

Follow us on twitter at @vulnifo to stay updated with new posts and information!

Visit our contact page for more ways to get in touch.

Leave a Comment

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply