Installing NextCloud
Installing NextCloud in the most traditional way, using system packages, and fixing configuration files. We will use NGINX and PosgreSQL, so that we get some familiarity with them too, although it can be installed with Apache2 and MariaDB as well. Most of the web applications can use both MariaDB and PostgreSQL as a DB, but some of them can use only one. NGINX and Apache2 can be use for any web application (either one or the other, usually not both at the same time).
1. Install Web Stack
1.1 Install NGINX
First let's stop and disable apache2
, because nginx
needs to
use the same ports (80
and 443
), so they can't run at the same
time:
systemctl stop apache2
systemctl status apache2
systemctl disable apache2
systemctl status apache2
systemctl mask apache2
systemctl status apache2
mv /var/www/html/index{,-apache2}.html
Now let's install nginx
:
apt install nginx
nginx -v
systemctl status nginx
To test it, open in browser http://188.245.242.143/ or http://www.user1.fs.al
1.2 Test PHP
We have installed PHP previously, let's test that it works with NGINX:
-
Edit
/etc/nginx/sites-enabled/default
and uncomment the PHP configuration:location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php-fpm.sock;
} -
Reload
nginx
configuration:nginx -t
systemctl reload nginx -
Create a test file
info.php
on the root of the webserver:ls /var/www/html/
echo "<?php phpinfo(); ?>" > /var/www/html/info.php
cat /var/www/html/info.php -
Try in browser http://www.user1.fs.al/info.php
-
Once you confirm that it works, remove the file
info.php
(for security reasons):rm /var/www/html/info.php
1.3 Enable HTTPS
-
Edit
/etc/nginx/sites-enabled/default
and uncomment these lines:listen 443 ssl default_server;
listen [::]:443 ssl default_server;
include snippets/snakeoil.conf;We have already installed the package
ssl-cert
, which generates the self-signed certificatessl-cert-snakeoil
.nano /etc/nginx/snippets/snakeoil.conf
-
Reload
nginx
configuration:nginx -t
systemctl reload nginx -
Open in browser HTTPS://www.user1.fs.al/
1.4 Setup LetsEncrypt
-
Create the config file
/etc/nginx/snippets/letsencrypt.conf
:cat <<'_EOF_' > /etc/nginx/snippets/letsencrypt.conf
location ^~ /.well-known/acme-challenge/ {
allow all;
default_type "text/plain";
root /var/www;
}
location = /.well-known/acme-challenge/ {
return 404;
}
_EOF_ -
Include it at
/etc/nginx/sites-enabled/default
:server {
# . . . . . . . . . .
include snippets/snakeoil.conf;
include snippets/letsencrypt.conf;
# . . . . . . . . . .
} -
Reload
nginx
:nginx -t
systemctl reload nginx -
Test getting a certificate with
certbot --dry-run
:certbot certonly \
--webroot -w /var/www \
-d nc1.user1.fs.al \
--dry-run
Removing comments
We can remove the comments and the empty lines from the config file like this:
cd /etc/nginx/
ls sites-available/
cp sites-available/default{,.bak}
ls sites-available/
grep '#' sites-available/default
sed sites-available/default -e '/#/ d'
sed sites-available/default -e '/#/ d' -e '/^$/ d'
sed -i sites-available/default -e '/#/d' -e '/^$/d'
nginx -t
systemctl reload nginx
After removing the comments (and changing the order of some lines), the config file should look like this:
server {
server_name _;
listen 80 default_server;
listen [::]:80 default_server;
listen 443 ssl default_server;
listen [::]:443 ssl default_server;
include snippets/snakeoil.conf;
include snippets/letsencrypt.conf;
root /var/www/html;
index index.html index.htm index.nginx-debian.html;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php-fpm.sock;
}
}
1.5 Install PostgreSQL
PostgreSQL uses a different port (5432
) from MariaDB (3306
), so
they can actually run at the same time. But let's stop MariaDB anyway,
since we don't need it right now:
systemctl stop mariadb
systemctl disable mariadb
systemctl mask mariadb
systemctl status mariadb
Install PostgreSQL:
apt install postgresql postgresql-contrib
systemctl status postgresql
pg_lsclusters
pg_isready
Try some psql
commands
su -l postgres
pwd
whoami
psql
\l
\l postgres
\x
\l postgres
select * from pg_user;
\l
\?
\q
2. Install NextCloud
2.1 Install PHP modules
PHP is already installed, but let's make sure that we also install some PHP modules that are needed by NextCloud or its plugins:
# packages needed by postgres and nginx
apt install \
php-pgsql \
php8.2-fpm
systemctl status php8.2-fpm
apt install \
php-common \
php-dev \
php-curl \
php-gd \
php-mbstring \
php-xml \
php-zip \
php-bz2 \
php-intl \
php-ldap \
php-imap \
php-bcmath \
php-gmp \
php-json \
php-pear \
php-soap \
php-apcu \
php-imagick \
libmagickcore-dev \
php-redis \
php-memcached
2.2 Create a Database
-
Create a database:
su -l postgres -c psql
CREATE DATABASE nc1db TEMPLATE template0 ENCODING 'UTF8';
\x
\l nc1 -
Create a user:
CREATE USER nc1user WITH PASSWORD 'nc1pass' CREATEDB;
select * from pg_user; -
Set the user as the owner of the database and grant all the privileges to it:
ALTER DATABASE nc1db OWNER TO nc1user;
GRANT ALL PRIVILEGES ON DATABASE nc1db TO nc1user;
GRANT ALL PRIVILEGES ON SCHEMA public TO nc1user;
\du
\q
2.3 Get NC Code
-
Download NC:
wget https://download.nextcloud.com/server/releases/latest.tar.bz2
tar xjf latest.tar.bz2
rm latest.tar.bz2
mv nextcloud/ /var/www/nc1
ls /var/www/ -
Fix ownership:
chown www-data:www-data -R /var/www/nc1
ls -al /var/www/nc1/
2.4 Create a Virtual Host
-
Create the file
/etc/nginx/sites-available/nc1
with a content like this:/etc/nginx/sites-available/nc1
upstream php-handler {
#server 127.0.0.1:9000;
server unix:/run/php/php8.2-fpm.sock;
}
# Set the `immutable` cache control options only for assets with a cache busting `v` argument
map $arg_v $asset_immutable {
"" "";
default ", immutable";
}
server {
listen 80;
listen [::]:80;
server_name nc1.user1.fs.al;
# Prevent nginx HTTP Server Detection
server_tokens off;
# Handle LetsEncrypt challenges
include snippets/letsencrypt.conf;
location ^~ / {
# Redirect HTTP to HTTPS
return 301 https://$server_name$request_uri;
}
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name nc1.user1.fs.al;
# Path to the root of your installation
root /var/www/nc1;
# Use Mozilla's guidelines for SSL/TLS settings
# https://mozilla.github.io/server-side-tls/ssl-config-generator/
ssl_certificate /etc/letsencrypt/live/nc1.user1.fs.al/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/nc1.user1.fs.al/privkey.pem;
# Prevent nginx HTTP Server Detection
server_tokens off;
# HSTS settings
# WARNING: Only add the preload option once you read about
# the consequences in https://hstspreload.org/. This option
# will add the domain to a hardcoded list that is shipped
# in all major browsers and getting removed from this list
# could take several months.
#add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload" always;
# set max upload size and increase upload timeout:
client_max_body_size 512M;
client_body_timeout 300s;
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 text/javascript application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/wasm 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;
# Pagespeed is not supported by Nextcloud, so if your server is built
# with the `ngx_pagespeed` module, uncomment this line to disable it.
#pagespeed off;
# The settings allows you to optimize the HTTP2 bandwidth.
# See https://blog.cloudflare.com/delivering-http-2-upload-speed-improvements/
# for tuning hints
client_body_buffer_size 512k;
# HTTP response headers borrowed from Nextcloud `.htaccess`
add_header Referrer-Policy "no-referrer" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Permitted-Cross-Domain-Policies "none" always;
add_header X-Robots-Tag "noindex, nofollow" always;
add_header X-XSS-Protection "1; mode=block" always;
# Remove X-Powered-By, which is an information leak
fastcgi_hide_header X-Powered-By;
# Set .mjs and .wasm MIME types
# Either include it in the default mime.types list
# and include that list explicitly or add the file extension
# only for Nextcloud like below:
include mime.types;
types {
text/javascript mjs;
# application/wasm wasm;
}
# Specify how to handle directories -- specifying `/index.php$request_uri`
# here as the fallback means that Nginx always exhibits the desired behaviour
# when a client requests a path that corresponds to a directory that exists
# on the server. In particular, if that directory contains an index.php file,
# that file is correctly served; if it doesn't, then the request is passed to
# the front-end controller. This consistent behaviour means that we don't need
# to specify custom rules for certain paths (e.g. images and other assets,
# `/updater`, `/ocs-provider`), and thus
# `try_files $uri $uri/ /index.php$request_uri`
# always provides the desired behaviour.
index index.php index.html /index.php$request_uri;
# Rule borrowed from `.htaccess` to handle Microsoft DAV clients
location = / {
if ( $http_user_agent ~ ^DavClnt ) {
return 302 /remote.php/webdav/$is_args$args;
}
}
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}
# Make a regex exception for `/.well-known` so that clients can still
# access it despite the existence of the regex rule
# `location ~ /(\.|autotest|...)` which would otherwise handle requests
# for `/.well-known`.
location ^~ /.well-known {
# The rules in this block are an adaptation of the rules
# in `.htaccess` that concern `/.well-known`.
location = /.well-known/carddav { return 301 /remote.php/dav/; }
location = /.well-known/caldav { return 301 /remote.php/dav/; }
location /.well-known/acme-challenge { try_files $uri $uri/ =404; }
location /.well-known/pki-validation { try_files $uri $uri/ =404; }
# Let Nextcloud's API for `/.well-known` URIs handle all other
# requests by passing them to the front-end controller.
return 301 /index.php$request_uri;
}
# Rules borrowed from `.htaccess` to hide certain paths from clients
location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)(?:$|/) { return 404; }
location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console) { return 404; }
# Ensure this block, which passes PHP files to the PHP process, is above the blocks
# which handle static assets (as seen below). If this block is not declared first,
# then Nginx will encounter an infinite rewriting loop when it prepends `/index.php`
# to the URI, resulting in a HTTP 500 error response.
location ~ \.php(?:$|/) {
# Required for legacy support
rewrite ^/(?!index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|ocs-provider\/.+|.+\/richdocumentscode(_arm64)?\/proxy) /index.php$request_uri;
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; # Avoid sending the security headers twice
fastcgi_param front_controller_active true; # Enable pretty urls
fastcgi_pass php-handler;
fastcgi_intercept_errors on;
fastcgi_request_buffering off;
fastcgi_max_temp_file_size 0;
}
# Serve static files
location ~ \.(?:css|js|mjs|svg|gif|ico|jpg|png|webp|wasm|tflite|map|ogg|flac)$ {
try_files $uri /index.php$request_uri;
# HTTP response headers borrowed from Nextcloud `.htaccess`
add_header Cache-Control "public, max-age=15778463$asset_immutable";
add_header Referrer-Policy "no-referrer" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Permitted-Cross-Domain-Policies "none" always;
add_header X-Robots-Tag "noindex, nofollow" always;
add_header X-XSS-Protection "1; mode=block" always;
access_log off; # Optional: Don't log access to assets
}
location ~ \.(otf|woff2?)$ {
try_files $uri /index.php$request_uri;
expires 7d; # Cache-Control policy borrowed from `.htaccess`
access_log off; # Optional: Don't log access to assets
}
# Rule borrowed from `.htaccess`
location /remote {
return 301 /remote.php$request_uri;
}
location / {
try_files $uri $uri/ /index.php$request_uri;
}
}For more details see: https://docs.nextcloud.com/server/stable/admin_manual/installation/nginx.html
DownloadDownload nc1-config.nginx
wget https://linux-cli.fs.al/apps/part2/nc1-config.nginx
mv nc1-config.nginx /etc/nginx/sites-available/nc1
nano /etc/nginx/sites-available/nc1 -
Link to it from
sites-enabled/
:ln -s ../sites-available/nc1 /etc/nginx/sites-enabled/nc1
ls -l /etc/nginx/sites-enabled/
nginx -tWe don't have yet an SSL cert.
-
Get an SSL cert from LetsEncrypt:
certbot certonly \
--webroot -w /var/www \
-d nc1.user1.fs.al \
--dry-run
certbot certonly \
--webroot -w /var/www \
-d nc1.user1.fs.al \
-m dashohoxha@gmail.com \
--agree-tos
certbot certificates -
Reload the
nginx
config:nginx -t
systemctl reload nginx
2.4 Run wizard
Open in browser https://nc1.user1.fs.al and give the required details about the admin account and database settings.
After the installation is finished, explore a little bit. Try to install another app. Create another user.
3. Improvements
If we check the overview of the admin settings, we will notice that it is reporting some errors and warnings that need to be fixed: https://nc1.user1.fs.al/settings/admin/overview
Let's start by trying to fix these, and then we may also make some other improvements.
3.1 Fix PHP memory limit
Edit /etc/php/8.2/fpm/php.ini
and set memory_limit
at least
512M
:
; Maximum amount of memory a script may consume
; https://php.net/memory-limit
memory_limit = 512M
Then reload the service php8.2-fpm
and check that the error is not
reported anymore:
systemctl reload php8.2-fpm
3.2 Create an alias for occ
It is a useful command for managing NextCloud from the terminal:
sudo -u www-data php /var/www/nc1/occ
But it is a bit awkward to run it like this. Let's create an alias for it:
alias occ='sudo -u www-data php /var/www/nc1/occ'
echo "alias occ='sudo -u www-data php /var/www/nc1/occ'" >> ~/.bashrc_custom
nano ~/.bashrc_custom
occ
occ | less
occ --ansi | less -r
Fix also /etc/hosts
Modify also /etc/hosts
and make sure that it contains this line:
127.0.1.1 mycloud
This will avoid a warning by sudo
. Here, mycloud
is the hostname
(the result of the command hostname
).
Add also lines like these to /etc/hosts
:
127.0.0.1 nc1.user1.fs.al
127.0.0.1 wp1.user1.fs.al
127.0.0.1 wp1.user1.fs.al
This way the apps won’t have to query the public DNS, which may improve the overall stability.
3.3 Corrections with occ
Set maintenance_window_start
:
occ config:system:get maintenance_window_start # get
occ config:system:set maintenance_window_start --type=integer --value=1
occ config:system:get maintenance_window_start
This command will actually add a new setting at the config file
/var/www/nc1/config/config.php
:
cd /var/www/nc1/
nano config/config.php
Make some more corrections with occ
:
occ maintenance:repair --include-expensive
occ db:add-missing-indices
occ config:system:set default_phone_region --value=AL
3.4 Running cron jobs
On the page https://nc1.user1.fs.al/settings/admin we see that the
recommended way for running background jobs is cron. We need to call
periodically php -f /var/www/nc1/cron.php
, as the user www-data
.
One way to do it is by using cron
and crontab
:
-
Edit the cron table of user
www-data
:crontab -u www-data -e
-
Append this line:
*/5 * * * * php -f /var/www/nc1/cron.php
However, let's do it instead with systemd
:
-
Create the file
/etc/systemd/system/nc1cron.service
:[Unit]
Description=nc1 cron.php job
[Service]
User=www-data
ExecCondition=php -f /var/www/nc1/occ status -e
ExecStart=/usr/bin/php -f /var/www/nc1/cron.php
KillMode=process -
Create the file
/etc/systemd/system/nc1cron.timer
:[Unit]
Description=Run nc1cron.service every 5 minutes
[Timer]
OnBootSec=5min
OnUnitActiveSec=5min
Unit=nc1cron.service
[Install]
WantedBy=timers.target -
Enable and start the timer:
systemctl enable --now nc1cron.timer
systemctl status nc1cron
Reload the page https://nc1.user1.fs.al/settings/admin and verify that NC now is using cron for the background jobs.
3.5 Fix PHP-FPM configuration
-
Edit
/etc/php/8.2/fpm/pool.d/www.conf
and uncomment these lines (near the end):env[PATH] = /usr/local/bin:/usr/bin:/bin
env[TMP] = /tmp -
Restart
php8.2-fpm
:systemctl restart php8.2-fpm
3.6 Enable OPCache
The PHP OPcache improves the performance of PHP applications by caching precompiled bytecode.
Edit /etc/php/8.2/fpm/php.ini
and uncomment/modify these lines:
[PHP]
zend_extension=opcache
[opcache]
opcache.enable=1
opcache.interned_strings_buffer=64
opcache.revalidate_freq=60
Then restart php8.2-fpm
:
systemctl restart php8.2-fpm
3.7 Change max upload size
-
Edit
/etc/nginx/sites-enabled/nc1
and modifyclient_max_body_size
:client_max_body_size 1024M;
Then reload
nginx
:systemctl reload nginx
-
Edit
/etc/php/8.2/fpm/php.ini
like this:; Maximum allowed size for uploaded files.
; https://php.net/upload-max-filesize
upload_max_filesize = 1024M
; Maximum size of POST data that PHP will accept.
; Its value may be 0 to disable the limit. It is ignored if POST data reading
; is disabled through enable_post_data_reading.
; https://php.net/post-max-size
post_max_size = 1088M
; Maximum amount of memory a script may consume
; https://php.net/memory-limit
memory_limit = 1600MThen reload
php8.2-fpm
:systemctl reload php8.2-fpm
3.8 Check the security
Check the security of your NC at: https://scan.nextcloud.com/
4. More improvements
The following modifications are not strictly required.
4.1 Setup email notifications
Go to "Basic settings": https://nc1.user1.fs.al/settings/admin
On the section "Mail server" set these values
- Send mode:
SMTP
- Enryption:
None/STARTTLS
- From address:
nc1@fs.al
- Server address:
smtp.fs.al:25
Then click "Send email" to test and verify the settings.
4.2 Improve memcache
-
Make sure that the needed packages are installed:
apt install php-apcu php-redis
apt install redis-server -
Edit (or uncomment) these lines at
/etc/redis/redis.conf
:port 0
unixsocket /run/redis/redis-server.sock
unixsocketperm 770Note: Make sure that the permissions are
770
, not700
.systemctl restart redis
systemctl status redis -
Add the user
www-data
to the groupredis
:usermod -aG redis www-data
-
Append
apc.enable_cli=1
to/etc/php/8.2/mods-available/apcu.ini
-
Add these lines at
/var/www/nc1/config/config.php
:'memcache.local' => '\OC\Memcache\APCu',
'memcache.locking' => '\OC\Memcache\Redis',
'redis' => array(
'host' => '/var/run/redis/redis-server.sock',
'port' => 0,
'timeout' => 0.0,
),
4.3 How ro reset a password
occ user --help
occ user:resetpassword --help
occ user:resetpassword admin
4.4 Enable HSTS
If "HTTP Strict Transport Security" is enabled, browsers will always try to use an HTTPS connection for accessing our website, and will refuse to open an HTTP (without S) page. It will also prevent site visitors from bypassing invalid certificate warnings.
This is recommended for enhanced security, but only after you are sure that you have set up everything else correctly.
It can be enabled by uncommenting this line at
/etc/nginx/sites-available/nc1
:
add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload" always;
Then reload the configuration of nginx:
nginx -t
systemctl reload nginx
4.5 Move the Data Directory
The data directory is at /var/www/nc1/data/
. We can move it to
/var/www/nc1-data/
like this:
occ maintenance:mode --help
occ maintenance:mode --on
mkdir -p /var/www/nc1-data/
chown www-data: /var/www/nc1-data/
cp -a /var/www/nc1/data/* /var/www/nc1-data/
cp -a /var/www/nc1/data/.ocdata /var/www/nc1-data/
nano /var/www/nc1/config/config.php # change 'datadirectory'
occ maintenance:mode --off
# rm -rf /var/www/nc1/data/