Nextcloud 31 Almalinux 9.6 LEMP Server stack; letsencypt certificate - Nginx 1.22 - PHP 8.4 - Postgresql 16 - Redis 6 - SELinux enforcing

Hi community,

This is a fully functioning and up-to-date guide for installing Nextcloud on a bare-metal server or VM running an RHEL clone.
I’m fed up with Debian — an OS should have support for as long as the hardware is in use!

Rocky Linux is great, but Almalinux seems to be the more stable option right now.

I have had Nginx and PostgreSQL on my servers for four years now, after running a Nextcloud server on Debian with Apache.
In fact, I never really understood Apache, and Nginx is easier to configure and debug.
RHEL and clones don’t have a user (www-data) that web servers (Apache or Nginx) on Debian use by default. ‘Nginx’ will be this user.

I’m just a final user who is passionate about Linux systems, so some of my choices could be debatable.
Any suggestions are welcome.

There is only one bug to fix: due to the deprecation of implicit nullable parameter declarations in PHP 8.4, the nullable

Abstract:
Nginx will connect to PHP-FPM via a UNIX socket. PostgreSQL and Redis are also configured to use a UNIX socket.
The URL of the Nextcloud server is https://subdomain.mydomain.com/nextcloud.
Installation of Almalinux is not addressed. Visit your provider account to create a subdomain and configure a dynamic DNS if needed. Also configure your router (port forwarding and hostname dyndns).

All commands are executed by root, meaning you have added your Almalinux user to the sudoers group.
user1 is this user below.
Vi is my favourite text editor!

I put SELinux in permissive mode, which disables it temporarily until the next reboot.
This ensures that there are no unwanted restrictions when installing the LEMP stack and Nextcloud.

temporarily place the system into permissive mode:

setenforce 0

get back to enforcing mode:

setenforce 1

In order to permanently change the SELinux mode to permissive:

sed -i 's/^SELINUX=.*/SELINUX=permissive/g' /etc/selinux/config

get back:

sed -i 's/^SELINUX=.*/SELINUX=enforcing/g' /etc/selinux/config
setenforce 1

The SELinux status can be checked as follows:

sestatus

It should be now in permissive mode.

I disable Firewall temporarily too.

systemctl stop firewalld
systemctl disable firewalld
systemctl status firewalld

get back:

systemctl enable firewalld
systemctl start firewalld

Installation LEMP Stack:
Let’s go!

After logging in, grant user1 root rights.

sudo -s

Update and install some usefull stuff:

dnf install epel-release
dnf update
dnf install wget tar bzip2 net-tools bind-utils msmtp s-nail unzip

add your web server address:

host -t A subdomain.mydomain.com
hostnamectl set-hostname nextcloud
hostnamectl

Configure msmtp a very simple mail server
This example is for OHV in France; you need to create the config file.

vi /etc/msmtprc
# Valeurs par défaut pour tous les comptes.
defaults
auth           on
tls            on
tls_starttls   on
tls_trust_file /etc/ssl/certs/ca-bundle.crt
logfile        /var/log/msmtp
aliases /etc/aliases

# Compte OVH avec starttls off
account        ovh
host           ssl0.ovh.net
port           465
from           everything@everything.com
user           youruser@mydomain.com
password       mystrongpasswd
tls_starttls   off
# Set a default account
account default : ovh
vi /etc/aliases
# Person who should get root's mail
root: youruser@mydomain.com
local: youruser@mydomain.com
default: youruser@mydomain.com

To send mails using the mail command, the package s-nail is needed, it also provides the mailx command.

You will also need to provide a sendmail-compatible MTA, either by installing msmtp-mta (which symlinks sendmail to msmtp) or by editing /etc/mail.rc to set the sendmail path:

vi /etc/mail.rc
set mta=/usr/bin/msmtp

Allow logs: no msmtp user or group as debian

touch /var/log/msmtp 
chown user1:root /var/log/msmtp

If you want to receive an email notification for logins, you could add this to your bashrc.
Edit the bash configuration and add this line at the end of the file (after fi): ` = AltGr-7

vi /etc/bashrc
echo ALERT - Shell Access on: My server `hostname -f` `date` `who` | mail -s "Alert: Shell Access on `hostname -f`" root

If you log out and then log back in, you should receive an email. This email will contain this alert.

Revert cron to noanacron, this step is important, even for nextcloud tasks

(cron - Automating Commands - Documentation)
Discussed briefly here is anacron in reference to the cron “dot” directories. cron runs by anacron, and is helpful for machines that are not up all the time, such as workstations and notebooks. The reason for this is that while cron runs jobs on a schedule, if the machine is off at the scheduled job time, the job does not run. With anacron the job will run when the machine is on again, even if the scheduled run was in the past. anacron though, uses a more randomized approach to running tasks where the timing is not exact. This makes sense for workstations and notebooks, but not for servers. This can be a problem for things such as server backups, for instance, needing to run a job at a specific time. That is where cron provides the best solution for server administrators.

dnf install cronie-noanacron
dnf remove cronie-anacron

Install Cerbot (linux pip) wildcard
augeas-devel is part of the powertool repository (AlmaLinux Repositories | AlmaLinux Wiki)

dnf config-manager --set-enabled crb 
dnf install python3 python-devel augeas-devel gcc

Install Certbot
Certbot Instructions
Set up a Python virtual environment

python3 -m venv /opt/certbot/
/opt/certbot/bin/pip install --upgrade pip
/opt/certbot/bin/pip install certbot certbot-nginx

Prepare the Certbot command

ln -s /opt/certbot/bin/certbot /usr/bin/certbot

Install correct DNS plugin
Run the following command, replacing with the name of your DNS provider.

/opt/certbot/bin/pip install certbot-dns-<PLUGIN>

For example, if your DNS provider is OVH you’d run the following command:

/opt/certbot/bin/pip install certbot-dns-ovh

(Welcome to certbot-dns-ovh’s documentation! — certbot-dns-ovh 0 documentation)
interface OVH pour créer des accès à l’API (Control panel - OVHcloud)
app name certbot-DNS-nextcloud
app description nextcloud
let certbot access API to renew certificate from Letsencrypt
Unlimited

GET /domain/zone/*
PUT /domain/zone/*
POST /domain/zone/*
DELETE /domain/zone/*

create a credential file

vi /root/.ovhapi
# OVH API credentials used by Certbot
dns_ovh_endpoint = ovh-eu
dns_ovh_application_key = akakakakakakakak
dns_ovh_application_secret = asasasasasasasasasasasasasasasas
dns_ovh_consumer_key = ckckckckckckckckckckckckckckckck
chmod 600 /root/.ovhapi

path to this file can be provided interactively or using the --dns-ovh-credentials

To acquire a certificate for example.com, waiting 120 seconds for DNS propagation

certbot certonly --dns-ovh --dns-ovh-credentials ~/.ovhapi --non-interactive --agree-tos --dns-ovh-propagation-seconds 120 --email youruser@mydomain.com -d subdomain.mydomain.com

Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/subdomain.mydomain.com/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/subdomain.mydomain.com/privkey.pem
This certificate expires on yyyy-mm-dd.
These files will be updated when the certificate renews.

Set up automatic renewal
(User Guide — Certbot 5.0.0.dev0 documentation)

SLEEPTIME=$(awk 'BEGIN{srand(); print int(rand()*(3600+1))}'); echo "0 0,12 * * * root sleep $SLEEPTIME && certbot renew -q" | tee -a /etc/crontab > /dev/null

If you needed to stop your webserver to run Certbot, you’ll want to add pre and post hooks to stop and start your webserver automatically. For example, if your webserver is Nginx, run the following commands to create the hook files in the appropriate directory:

sh -c 'printf "#!/bin/sh\nservice nginx stop\n" > /etc/letsencrypt/renewal-hooks/pre/nginx.sh'
sh -c 'printf "#!/bin/sh\nservice nginx start\n" > /etc/letsencrypt/renewal-hooks/post/nginx.sh'
chmod 755 /etc/letsencrypt/renewal-hooks/pre/nginx.sh
chmod 755 /etc/letsencrypt/renewal-hooks/post/nginx.sh

check:

vi /etc/crontab

SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root

# For details see man 4 crontabs

# Example of job definition:
# .---------------- minute (0 - 59)
# |  .------------- hour (0 - 23)
# |  |  .---------- day of month (1 - 31)
# |  |  |  .------- month (1 - 12) OR jan,feb,mar,apr ...
# |  |  |  |  .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# |  |  |  |  |
# *  *  *  *  * user-name  command to be executed

0 0,12 * * * root sleep 1357 && certbot renew -q

install Nginx AppStream module
Module version are getting the security updates. The recommended method. (A02 R92 ❯ AlmaLinux 9.2 Installation | AlmaLinux Wiki)

dnf module list nginx
dnf module enable nginx:1.22
dnf install nginx
systemctl enable nginx
systemctl start nginx
systemctl status nginx 

webserver test page: IpV4 of your server

http://aaa.bbb.ccc.ddd/ 
netstat -plant | grep '80\|443'
netstat -plunt | grep nginx

Install Redis
(Install Redis on Linux | Docs)

dnf install redis
systemctl enable redis
redis-server --version
Redis server v=6.2.18 sha=00000000:0 malloc=jemalloc-5.1.0 bits=64 build=797283b6387a0075
systemctl start redis
systemctl status redis
chown -R redis:redis /var/log/redis
chmod -R u+rwX,g+rwX,u+rx /var/log/redis

Install PHP8.4 (ready for nextcloud v31)

dnf module list php

php 8.4 is not in appstream 9.6

Operating system and version selection
Operating system: EL9
Wanted PHP version: 8.4.8 (active support until 11/2026
Type of installation: single
Architecture: x86_64
Wizard answer

EL 9 provides PHP version 8.0, 8.1, 8.2 in its official repository

Command to enabled the CRB repository:

dnf config-manager --set-enabled crb

Command to install the EPEL repository configuration package:

dnf install https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm  (déjà installé)

import the GnuPG public key of the key pair used to sign my packages (Fedora 34-35 and EL-9) https://rpms.remirepo.net/

curl -sSLo RPM-GPG-KEY-remi2021 https://rpms.remirepo.net/RPM-GPG-KEY-remi2021
rpm --import RPM-GPG-KEY-remi2021

Command to install the Remi repository configuration package:

dnf install https://rpms.remirepo.net/enterprise/remi-release-9.rpm

Install php modules for Nextcloud:

dnf install php-{gmp,sodium,pgsql,gd,posix,zip,intl,smbclient,redis,imagick,imap,opcache}

Install additional package for memcachelocal APCu

dnf install php84-php-pecl-apcu

Enable php-fpm:

systemctl enable --now php-fpm.service
systemctl start php-fpm
systemctl status php-fpm

list of php modules:

php -m list installed
php84 --modules
dnf list --installed | grep php

__Optimise php 1
To find out how many CPU cores your server has, run the following command:

echo Cores = $(( $(lscpu | awk '/^Socket/{ print $2 }') * $(lscpu | awk '/^Core/{ print $4 }') ))

When determining how much memory you can dedicate to PHP, keep in mind that the server is also running Nginx and PostgreSQL. How much memory are these other processes consuming?
For example, if you have 4GB of RAM and the other processes are consuming 1GB, that leaves you with 3GB – or 2GB if you want to play safe.

Finally, to get a general idea on how much memory each PHP process is consuming, run:

ps --no-headers -o "rss,cmd" -C php-fpm | awk '{ sum+=$1 } END { printf ("%d%s\n", sum/NR/1024,"M") }'

Edit php-fpm config:
search and modify all values

vi /etc/php-fpm.d/www.conf :

To get a good value for pm.max_children, take the memory that you want to allocate to PHP and divide it by the average memory that is consumed by each PHP process.
For example, if you want to allocate 2.5GB (2500MB) and each process consumes about 50MB. Dividing 2500 by 50 we get around 50.
So, set pm.max_children to 50.

For pm.start_servers, multiply the number of cores that you have by 4.
If you have 2 cores: 2 x 4 = 8
So, set pm.start_servers to 8.

For pm.min_spare_servers, multiply the number of cores that you have by 2.
If you have 2 cores: 2 x 2 = 4
So, set pm.min_spare_servers to 4.

For pm.max_spare_servers, multiply the number of cores on your server by 4.
If you have 2 cores: 2 x 4 = 8
So, set pm.max_spare_servers to 8. The same used before for pm.start_servers.

uncomment lines that refer to PATH and TMP, like this:

env[PATH] = /usr/local/bin:/usr/bin:/bin
env[TMP] = /tmp

__Optimise php 2

vi /etc/php.ini
date.timezone = Europe/Paris
memory_limit = 512M
upload_max_filesize = 200M
post_max_size = 200M
# increase the timeoute value to avoid; ConnectException cURL error 28
max_execution_time = 600

# disable cgi.fix https://www.php.net/manual/fr/install.unix.nginx.php
cgi.fix_pathinfo=0
vi /etc/php.d/10-opcache.ini

opcache.memory_consumption=128
opcache.interned_strings_buffer=32
opcache.max_accelerated_files=10000
opcache.revalidate_freq=60
opcache.save_comments = 1

Restart php-fpm:

systemctl restart php-fpm

Install Postgresql 16

dnf module list postgresql
dnf module enable postgresql:16
dnf install postgresql-server postgresql-contrib
postgres -V

Initialize the Database:
create the necessary directory structure and configuration files for the PostgreSQL database

/usr/bin/postgresql-setup --initdb --unit postgresql

You will be welcome if you know how to install the database in a custom directory, such as /opt/postgresql/, rather than in the default directory of /var/lib/pgsql.

Start and Enable PostgreSQL Service:

systemctl enable postgresql
systemctl start postgresql
systemctl status postgresql

optimise Database https://pgtune.leopard.in.ua/

vi /var/lib/pgsql/data/postgresql.conf

DB Version: 16
OS Type: linux
DB Type: web
Total Memory (RAM): 8 GB
CPUs num: 4
Connections num: 20
Data Storage: san

max_connections = 20
shared_buffers = 2GB
effective_cache_size = 6GB
maintenance_work_mem = 512MB
checkpoint_completion_target = 0.9
wal_buffers = 16MB
default_statistics_target = 100
random_page_cost = 1.1
effective_io_concurrency = 300
work_mem = 87381kB
huge_pages = off
min_wal_size = 1GB
max_wal_size = 4GB
max_worker_processes = 4
max_parallel_workers_per_gather = 2
max_parallel_workers = 4
max_parallel_maintenance_workers = 2
systemctl restart postgresql

create password for user postgres:

su postgres
bash-5.1$ psql
postgres=# ALTER USER postgres WITH PASSWORD 'myverystrongpasswd';
postgres=# \q
bash-5.1$ exit
systemctl restart postgresql

switch to a UNIX-Socket instead of using TCP.
postgres server don’t accept/listen from any network (i.e. TCP/IP 4/6) connections.

sudo -i -u postgres psql
postgres=# ALTER SYSTEM SET listen_addresses TO '';
postgres=# \q

If the list is empty, the server does not listen on any IP interface at all, in which case only Unix-domain sockets can be used to connect to it.
(https://www.postgresql.org/docs/current/runtime-config-connection.html)

Restart psql:

systemctl restart postgresql

Eventually, ALTER SYSTEM writes the given parameter setting to the /var/lib/pgsql/data/postgresql.auto.conf file,
which is read in addition to postgresql.conf

DB Version: 16
OS Type: linux
DB Type: web
Total Memory (RAM): 8 GB
CPUs num: 4
Connections num: 20
Data Storage: san

ALTER SYSTEM SET
max_connections = ‘20’;
ALTER SYSTEM SET
shared_buffers = ‘2GB’;
ALTER SYSTEM SET
effective_cache_size = ‘6GB’;
ALTER SYSTEM SET
maintenance_work_mem = ‘512MB’;
ALTER SYSTEM SET
checkpoint_completion_target = ‘0.9’;
ALTER SYSTEM SET
wal_buffers = ‘16MB’;
ALTER SYSTEM SET
default_statistics_target = ‘100’;
ALTER SYSTEM SET
random_page_cost = ‘1.1’;
ALTER SYSTEM SET
effective_io_concurrency = ‘300’;
ALTER SYSTEM SET
work_mem = ‘87381kB’;
ALTER SYSTEM SET
huge_pages = ‘off’;
ALTER SYSTEM SET
min_wal_size = ‘1GB’;
ALTER SYSTEM SET
max_wal_size = ‘4GB’;
ALTER SYSTEM SET
max_worker_processes = ‘4’;
ALTER SYSTEM SET
max_parallel_workers_per_gather = ‘2’;
ALTER SYSTEM SET
max_parallel_workers = ‘4’;
ALTER SYSTEM SET
max_parallel_maintenance_workers = ‘2’;

create database for unix socket:

user: myncuser
database name: nextclouddb

user and database name will be used when installing nextcloud (web interface)
(https://docs.nextcloud.com/server/latest/admin_manual/configuration_database/linux_database_configuration.html?highlight=sql#postgresql-database)
(PostgreSQL: Documentation: 16: 21.1. The pg_hba.conf File)

sudo -u postgres psql -d template1

Then a template1=# prompt will appear. Now enter the following lines and confirm them with the enter key:

CREATE USER myncuser CREATEDB;
CREATE DATABASE nextclouddb OWNER myncuser TEMPLATE template0 ENCODING 'UTF8';
GRANT CREATE ON SCHEMA public TO myncuser;

list database:

postgres=# \l or \l nextcloud

List all users and roles with:

postgres=# \du

You can quit the prompt by entering:

postgres=# \q

Restart psql:

systemctl restart postgresql

A Nextcloud instance configured with PostgreSQL would contain the path to the socket on which the database is running as the hostname, the system username the PHP process is using, and an empty password to access it, and the name of the database.
The config/config.php as created by the Installation wizard would therefore contain entries like this:

<?php

  "dbtype"        => "pgsql",
  "dbname"        => "nextcloud",
  "dbuser"        => "nc",
  "dbpassword"    => "",
  "dbhost"        => "/var/run/postgresql",
  "dbtableprefix" => "oc_",

Note

The host actually points to the socket that is used to connect to the database. Using localhost here will not work if postgreSQL is configured to use peer authentication. Also note that no password is specified, because this authentication method doesn’t use a password.

find directory of the socket:

cd /var/lib/pgsql/data/log
vi day.log

listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432" 
listening on Unix socket "/tmp/.s.PGSQL.5432"

list all TCP or UDP ports that are being listened on, including the services using the ports and the socket status use the following command:

netstat -tunlp
ss -tunlp

postgresql doesn’t listen on any TCP address or port

replace peer with trust in pg_hba.conf at unix domain socket communication:

vi /var/lib/pgsql/data/pg_hba.conf
# Allow any user on the local system to connect to any database with
# any database user name using Unix-domain sockets (the default for local
# connections).
#
# "local" is for Unix domain socket connections only
#local   all             all                                     peer
local   all             all                                     trust

Restart psql:

systemctl restart postgresql

Nextcloud Nginx configuration /etc/nginx/nginx.conf read config in /etc/nginx/conf.d/nextcloud.conf
It is highly recommended to place your data directory outside of the Web root (i.e. outside of /var/www)
Nextcloud files are located at /var/www/nextcloud and the Nextcloud instance is accessed via http(s)://subdomain.domain.com/nextcloud/
Nextcloud in a subdir of the NGINX webroot

PHP-Handler Configuration / Avoiding “502 Bad Gateway”
If PHP FPM will be running on the same host as NGINX (it’s probably a safe assumption it will be if you’re unsure), it is recommended you use the UNIX socket (i.e. /var/run/php/php-fpm.sock) rather than TCP (127.0.0.1:9000) for maximum performance (though either will work as long as your NGINX and PHP FPM configurations match).
After deciding how you’d prefer to connect NGINX with PHP FPM (and, if necessary, updating your local PHP FPM configuration and restarting FPM), set your NGINX configuration’s upstream php-handler server to match your preference (Note: If using UNIX sockets, prepend unix: in the NGINX configuration, but not in your PHP FPM www.conf).

configuration for nc 31, deletion of the wasm extension already included in /etc/nginx/mime.types

vi /etc/nginx/conf.d/nextcloud.conf
upstream php-handler {
    #server 127.0.0.1:9000;
    server unix:/run/php-fpm/www.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 subdomain.mydomain.com;

    # Prevent nginx HTTP Server Detection
    server_tokens off;

    # Enforce HTTPS just for `/nextcloud`
    location /nextcloud {
        return 301 https://$server_name$request_uri;
    }
}

server {
    listen 443      ssl http2;
    listen [::]:443 ssl http2;
    # With NGinx >= 1.25.1 you should use this instead:
    # listen 443      ssl;
    # listen [::]:443 ssl;
    # http2 on;
    server_name subdomain.mydomain.com;

    # Path to the root of the domain
    root /var/www;

    # Use Mozilla's guidelines for SSL/TLS settings **NOT INCLUDED IN THIS HOWTO**
    # https://mozilla.github.io/server-side-tls/ssl-config-generator/
    ssl_certificate     /etc/letsencrypt/live/subdomain.mydomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/subdomain.mydomain.com/privkey.pem;

    # Prevent nginx HTTP Server Detection
    server_tokens off;

    # 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:
	# I remove types application/wasm wasm; already included in /etc/nginx/mime.types; to advoid 
	# nginx -t [warn] duplicate extension "wasm", content type: "application/wasm", previous content type: "application/wasm" in /etc/nginx/conf.d/nextcloud.conf:53
    include mime.types;
    types {
        text/javascript mjs;
	    }

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

    location ^~ /.well-known {
        # The rules in this block are an adaptation of the rules
        # in the Nextcloud `.htaccess` that concern `/.well-known`.

        location = /.well-known/carddav { return 301 /nextcloud/remote.php/dav/; }
        location = /.well-known/caldav  { return 301 /nextcloud/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 /nextcloud/index.php$request_uri;
    }

    location ^~ /nextcloud {
        # 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;

        # 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=31536000; includeSubDomains; preload" always;
		add_header Strict-Transport-Security "max-age=15552000; includeSubDomains; preload" always;

        # 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;

        # Specify how to handle directories -- specifying `/nextcloud/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/ /nextcloud/index.php$request_uri`
        # always provides the desired behaviour.
        index index.php index.html /nextcloud/index.php$request_uri;

        # Rule borrowed from `.htaccess` to handle Microsoft DAV clients
        location = /nextcloud {
            if ( $http_user_agent ~ ^DavClnt ) {
                return 302 /nextcloud/remote.php/webdav/$is_args$args;
            }
        }

        # Rules borrowed from `.htaccess` to hide certain paths from clients
        location ~ ^/nextcloud/(?:build|tests|config|lib|3rdparty|templates|data)(?:$|/)    { return 404; }
        location ~ ^/nextcloud/(?:\.|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
        # `/nextcloud/index.php` to the URI, resulting in a HTTP 500 error response.
        location ~ \.php(?:$|/) {
            # Required for legacy support
            rewrite ^/nextcloud/(?!index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|ocs-provider\/.+|.+\/richdocumentscode(_arm64)?\/proxy) /nextcloud/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 /nextcloud/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 /nextcloud/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 /nextcloud/remote {
            return 301 /nextcloud/remote.php$request_uri;
        }

        location /nextcloud {
            try_files $uri $uri/ /nextcloud/index.php$request_uri;
        }
    }
}

nginx server config

Check the username who runs nginx:

ps aux | grep nginx
nginx > user nginx
vi /etc/nginx/nginx.conf
# For more information on configuration, see:
#   * Official English Documentation: http://nginx.org/en/docs/
#   * Official Russian Documentation: http://nginx.org/ru/docs/

user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
pid /run/nginx.pid;

# Load dynamic modules. See /usr/share/doc/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;

events {
    worker_connections 1024;
}

http {
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile            on;
    tcp_nopush          on;
    keepalive_timeout   65;
    types_hash_max_size 4096;

    include             /etc/nginx/mime.types;
    default_type        application/octet-stream;

    # Load modular configuration files from the /etc/nginx/conf.d directory.
    # See http://nginx.org/en/docs/ngx_core_module.html#include
    # for more information.
    include /etc/nginx/conf.d/*.conf;

    server {
        listen       80;
        listen       [::]:80;
        server_name  _;
        root         /usr/share/nginx/html;

        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;

        error_page 404 /404.html;
        location = /404.html {
        }

        error_page 500 502 503 504 /50x.html;
        location = /50x.html {
        }
    }
}

# Settings for a TLS enabled server.  **NOT INCLUDED IN THIS HOWTO**
#
#    server {
#        listen       443 ssl http2;
#        listen       [::]:443 ssl http2;
#        server_name  _;
#        root         /usr/share/nginx/html;
#
#        ssl_certificate "/etc/pki/nginx/server.crt";
#        ssl_certificate_key "/etc/pki/nginx/private/server.key";
#        ssl_session_cache shared:SSL:1m;
#        ssl_session_timeout  10m;
#        ssl_ciphers PROFILE=SYSTEM;
#        ssl_prefer_server_ciphers on;
#
#        # Load configuration files for the default server block.
#        include /etc/nginx/default.d/*.conf;
#
#        error_page 404 /404.html;
#        location = /404.html {
#        }
#
#        error_page 500 502 503 504 /50x.html;
#        location = /50x.html {
#        }
#    }

}

Now verify the configuration syntax above:

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

Modify the www.conf file located in /etc/php-fpm.d/ directory,find user and group and replace apache with nginx as shown below.

vi /etc/php-fpm.d/www.conf   :set nu lign 24,27,50,51
listen = /run/php-fpm/www.sock
;listen = 127.0.0.1:9000
...
; RPM: apache user chosen to provide access to the same directories as httpd
user = nginx
; RPM: Keep a group allowed to write in log dir.
group = nginx
...
listen.owner = nginx
listen.group = nginx
listen.mode = 0660

NGINX configuration
If PHP FPM will be running on the same host as NGINX (it’s probably a safe assumption it will be if you’re unsure), it is recommended you use the UNIX socket (i.e. /var/run/php/php-fpm.sock) rather than TCP (127.0.0.1:9000) for maximum performance (though either will work as long as your NGINX and PHP FPM configurations match).

for almalinux it’s/run/php-fpm/www.sock

Restart Nginx Web server:

systemctl restart nginx

Configure Redis to listen on an unix socket

we usually make the redis user a member of the nginx group:

usermod -a -G redis nginx

check:

grep redis /etc/group
redis:x:995:nginx
getent group

give the rights

chmod +r /etc/redis/redis.conf

configure Redis to listen on an Unix socket, Memory caching
which is recommended if Redis is running on the same system as Nextcloud

vi /etc/redis/redis.conf
#port 6379
port 0
unixsocket /var/run/redis/redis.sock
#unixsocketperm 700
unixsocketperm 770
requirepass anystrongpasswdforredis

check:

cat /etc/redis/redis.conf | egrep "^#* *port +|^#* *unixsocket +|^#* *unixsocketperm +"
ps ax | grep redis

cat /var/www/nextcloud/config/config.php | \egrep "'filelocking\.enabled|'memcache\.local\ |'memcache\.locking|'host|'port|'timeout|'memcache\.distributed"

WARNING Memory overcommit must be enabled! Without it, a background save or replication may fail under low memory condition. Being disabled, it can can also cause failures without low memory condition, see vm.max_map_count growing steadily when vm.overcommit_memory is 2 · Issue #1328 · jemalloc/jemalloc · GitHub. To fix this issue add ‘vm.overcommit_memory = 1’ to /etc/sysctl.conf and then reboot or run the command ‘sysctl vm.overcommit_memory=1’ for this to take effect.

sysctl vm.overcommit_memory=1
vi /etc/sysctl.conf
vm.overcommit_memory = 1

Install Nextcloud outside webroot of Nginx and data dir folder outside /var/www:

cd /var/www/
wget https://download.nextcloud.com/server/releases/latest-31.tar.bz2
wget https://download.nextcloud.com/server/releases/latest-31.tar.bz2.sha256
# compare
sha256sum latest-31.tar.bz2
# and
cat latest-31.tar.bz2.sha256

the checksum on the nextcloud mirror matches the checksum of the file that we downloaded, meaning the integrity is good

mkdir -p /var/www/nextcloud
tar -xvf latest-31.tar.bz2 -C /var/www/
chown -R nginx:nginx nextcloud
rm latest-31.tar.bz2
cd /opt
mkdir -p /opt/nextcloud/data
chown -R nginx:nginx nextcloud

check file and directory rights:

find nextcloud/ -type f -exec chmod 640 {} ;
find nextcloud/ -type d -exec chmod 750 {} ;

Logs

tail /var/www/nextcloud/data/nextcloud.log
tail /var/log/nginx/access.log
tail /var/log/nginx/error.log

Create Nextcloud conf file:

cd /var/www/nextcloud/config/
vi config.php
<?php
$CONFIG = [
'instanceid' => '',
'passwordsalt' => '',
'secret' => '',
'trusted_domains' =>
   [
    'subdomain.mydomain.com',
	'localhost'
  ],
'overwrite.cli.url' => 'https://subdomain.mydomain.com/nextcloud',
'datadirectory' => '/opt/nextcloud/data',
'version' => '',
'dbtype' => '',
'dbhost' => '',
'dbport' = > '',
'dbname' => '',
'dbuser' => '',
'dbpassword' => '',
'dbtableprefix' => 'oc_',
'installed' => false,
'maintenance' => false,
'forbidden_filenames' =>
    [
	 'Thumbs.db',
	 'thumbs.db',
  ],
'skeletondirectory' => '',
'templatedirectory' => '',
'default_phone_region' => 'FR',
'default_timezone' => 'Europe/Paris',
'mail_smtpmode' => '',
'mail_sendmailmode' => '',
'mail_smtpauth' => true,
'mail_smtphost' => '',
'mail_smtpport' => '',
"mail_smtptimeout"  => 30,
'mail_smtpname' => '',
'mail_smtppassword' => '',
'mail_from_address' => '',
'mail_domain' => '',
'mail_smtpsecure' => '',
'maintenance_window_start' => 1,
'memcache.local' => `\OC\Memcache\APCu`,
'memcache.locking' => '\\OC\\Memcache\\Redis',
'redis' => [
	'host' => '/var/run/redis/redis.sock',
	'port' => 0,
	'timeout' => 0.0,
	'read_timeout' => 0.0,
	'password' => 'anystrongpasswdforredis',
	]
];

give nginx user write permissions:

chmod 640 config.php
chown nginx:nginx config.php

give execution rights to the cron.php script:

 cd /var/www/nextcloud/
-rw-r--r--
chmod 755 cron.php (ou chmod +x cron.php)
-rwxr-xr-x

memcache.local APCu is faster than Redis but you need to increase the memory in php.ini with the apc.smh_size parameter, as 32M is not enough.
Memory caching

module configuration:

vi /etc/php.d/40-apcu.ini
apc.shm_size=128M

Restart php:

systemctl restart php-fpm

You must give folder rights to the ngninx group (otherwise nextcloud cannot access the session folder and there is no login possible).

cd /var/lib/php
ls -A -l
total 4
drwxrwx---. 2 root apache    6 Jun  4 08:17 opcache
drwxr-xr-x. 2 root root   4096 Jun 22 20:18 peclxml
drwxrwx---. 2 root apache    6 Jun  4 08:17 session
drwxrwx---. 2 root apache    6 Jun  4 08:17 wsdlcache

chgrp nginx /var/lib/php/opcache
chgrp nginx /var/lib/php/session
chgrp nginx /var/lib/php/wsdlcache

After any modification to the redis, nginx or php configuration files, these services must be restarted.

systemctl restart redis nginx php-fpm

installing nc 31 from the web interface

https://subdomain.mydomain.com/nextcloud

Create admin account
Username myadmin
Password myveryverystrongpasswd

Configure the database postgresql
Database user myncuser
Database password leave it empty
Database name nextclouddb
/var/run/postgresql

if “instance” is not created during installation, the Nextcloud interface will not load

it must be created manually:

php /var/www/nextcloud/occ config:system:delete instanceid

if you delete it, a new one will be created automatically.

other checks to be made before the first admin login:

php /var/www/nextcloud/occ setupchecks

Run the recommended commands. Then continue.

and check nginx rights:

find /var/lib/php -group root

chown root:nginx /var/lib/php/peclxml/php-*
find /opt/nextcloud/data -group root

chown nginx:nginx /opt/nextcloud/data/appdata_INSTANCEID/js
chown nginx:nginx /opt/nextcloud/data/appdata_INSTANCEID/preview

Check if there is a cron job for user nginx:

crontab -u nginx -l

if missing:

crontab -u nginx -e
 */5  *  *  *  * php -f /var/www/nextcloud/cron.php
 :w
 :q

run cron.php:

sudo -u nginx php -f /var/www/nextcloud/cron.php

Display the list of background tasks:

sudo -u nginx php /var/www/nextcloud/occ background-job:list

finally log in to the NC web page with the admin account:
myncuser
myveryverystrongpasswd

If all is working, reactivate SELinux:

setenforce 1
sestatus

create rules for nexcloud:
SELinux configuration

semanage fcontext -a -t httpd_sys_rw_content_t '/opt/nextcloud/data(/.*)?'
restorecon -Rv '/opt/nextcloud/data'
ls -Zd /opt/nextcloud/data
semanage fcontext -a -t httpd_sys_rw_content_t '/var/www/nextcloud/config(/.*)?'
semanage fcontext -a -t httpd_sys_rw_content_t '/var/www/nextcloud/apps(/.*)?'
semanage fcontext -a -t httpd_sys_rw_content_t '/var/www/nextcloud/.htaccess'
semanage fcontext -a -t httpd_sys_rw_content_t '/var/www/nextcloud/.user.ini'
semanage fcontext -a -t httpd_sys_rw_content_t '/var/www/nextcloud/3rdparty/aws/aws-sdk-php/src/data/logs(/.*)?'
restorecon -Rv '/var/www/nextcloud/'
ls -Zd /var/www/nextcloud
  • Allow access to a remote database

An additional setting is needed if your installation is connecting to a remote database:

setsebool -P httpd_can_network_connect_db on
  • Allow access to remote network

Nextcloud requires access to remote networks for functions such as Server-to-Server sharing, external storages or the app store. To allow this access use the following setting:

setsebool -P httpd_can_network_connect on
  • Allow access to network memcache

This setting is not required if httpd_can_network_connect is already on:

 setsebool -P httpd_can_network_memcache on
  • Allow access to SMTP/sendmail

If you want to allow Nextcloud to send out e-mail notifications via sendmail you need to use the following setting:

setsebool -P httpd_can_sendmail on
  • Allow access to CIFS/SMB

If you have placed your datadir on a CIFS/SMB share use the following setting:

setsebool -P httpd_use_cifs on
  • Enable updates via the web interface
    If you want to unify HTTPD handling of all content files, you must turn on the httpd_unified boolean.
setsebool -P httpd_unified 1 

To enable updates via the web interface, you may need this to enable writing to the directories:

setsebool httpd_unified on

When the update is completed, disable write access:

setsebool -P  httpd_unified  off
  • Disallow write access to the whole web directory

For security reasons it’s suggested to disable write access to all folders in /var/www/ (default):

setsebool -P  httpd_unified  off

The firewall should be reactivated if necessary:

systemctl enable firewalld
systemctl start firewalld
systemctl status firewalld
firewall-cmd --list-services

#send mail
firewall-cmd --add-service=smtp --permanent

#ensure that your firewall does not block port 80 to the server and port 443 for SSL.
firewall-cmd --permanent --add-service={http,https}

firewall-cmd --reload

Don’t forget to secure or disable SHH, and install Fail2ban, Reaction or any other solution of your choice.
Hardening and security guidance

To prevent problems, only use the cli to update your NC instance:
Using the command line based updater

sudo -u nginx php /var/www/nextcloud/updater/updater.phar --no-backup

sudo -u nginx php /var/www/nextcloud/occ db:add-missing-indices

Fixing Error PHP at login in Nextcloud server logs
This error is related to a major change in php 8.4 and impacts the file external module of nc

PHP Error
Icewind\SMB\Native\NativeState::lseek(): Implicitly marking parameter $path as nullable is deprecated, the explicit nullable type must be used instead at /var/www/nextcloud/apps/files_external/3rdparty/icewind/smb/src/Native/NativeState.php#346

install composer:
(Composer)

cd
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php -r "if (hash_file('sha384', 'composer-setup.php') === 'dac665fdc30fdd8ec78b38b9800061b4150413ff2e3b6f88543c636f7cd84f6db9189d43a81e5503cda447da73c7e5b6') { echo 'Installer verified'.PHP_EOL; } else { echo 'Installer corrupt'.PHP_EOL; unlink('composer-setup.php'); exit(1); }"
php composer-setup.php
php -r "unlink('composer-setup.php');"
echo "${PATH//:/$'\n'}"
mv composer.phar /usr/bin/composer

Install php rector:
(https://github.com/rectorphp/rector)

# cd
# dnf install unzip
# exit

Install rector in home directory of user1, not as root:
(How do I install untrusted packages safely? Is it safe to run Composer as superuser or root? - Composer)

$ composer require --dev rector/rector
$ sudo -s
# cd /var/www/nextcloud/apps/files_external/3rdparty/icewind/smb

Copy this minimal configuration in rector.php:

# vi rector.php
<?php

return Rector\Config\RectorConfig::configure()
    ->withPaths([__DIR__ . '/src'])
    ->withPhpVersion(Rector\ValueObject\PhpVersion::PHP_84)
    ->withRules([
         Rector\Php84\Rector\Param\ExplicitNullableParamTypeRector::class,
    ]);

Run rector:

# /home/user1/vendor/bin/rector

 65/65 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%
2 files with changes
====================

1) src/Exception/RevisionMismatchException.php:9

    ---------- begin diff ----------
@@ @@
 use Throwable;

 class RevisionMismatchException extends Exception {
-       public function __construct(string $message = 'Protocol version mismatch', int $code = 0, Throwable $previous = null) {
+       public function __construct(string $message = 'Protocol version mismatch', int $code = 0, ?Throwable $previous = null) {
                parent::__construct($message, $code, $previous);
        }
 }
    ----------- end diff -----------

Applied rules:
 * ExplicitNullableParamTypeRector


2) src/Native/NativeState.php:343

    ---------- begin diff ----------
@@ @@
         *
         * @return false|int new file offset as measured from the start of the file on success.
         */
-       public function lseek($file, int $offset, int $whence = SEEK_SET, string $path = null) {
+       public function lseek($file, int $offset, int $whence = SEEK_SET, ?string $path = null) {
                if (!$this->state) {
                        throw new ConnectionException("Not connected");
                }
    ----------- end diff -----------

Applied rules:
 * ExplicitNullableParamTypeRector

next one 08/16/2025

SearchDAV\Query\Scope::__construct(): Implicitly marking parameter $path as nullable is deprecated, the explicit nullable type must be used instead at /var/www/nextcloud/3rdparty/icewind/searchdav/src/Query/Scope.php#54

# cd /var/www/nextcloud/3rdparty/icewind/searchdav
cp /var/www/nextcloud/apps/files_external/3rdparty/icewind/smb/rector.php rector.php

# /home/user1/vendor/bin/rector

It’s fixed

These steps must be repeated after each NC update, as long as the module’s php syntax has not been corrected by dev.

I created a thread and got expert answers regarding compatibility issues with PHP 8.4. Requests were made on GitHub, and ernolf added a “patch” script to his collection.
Thanks to him.

(PHP 8.4 implicit nullable parameter deprecation in NC31 3rdparty module icewind)

last but not least:

Install Fail2ban
(Hardening and security guidance — Nextcloud latest Administration Manual latest documentation)

dnf install -y fail2ban-server fail2ban-firewalld
vi /etc/fail2ban/filter.d/nextcloud.conf
[Definition]
_groupsre = (?:(?:,?\s*"\w+":(?:"[^"]+"|\w+))*)
failregex = ^\{%(_groupsre)s,?\s*"remoteAddr":"<HOST>"%(_groupsre)s,?\s*"message":"Login failed:
            ^\{%(_groupsre)s,?\s*"remoteAddr":"<HOST>"%(_groupsre)s,?\s*"message":"Two-factor challenge failed:
            ^\{%(_groupsre)s,?\s*"remoteAddr":"<HOST>"%(_groupsre)s,?\s*"message":"Trusted domain error.
datepattern = ,?\s*"time"\s*:\s*"%%Y-%%m-%%d[T ]%%H:%%M:%%S(%%z)?"
vi /etc/fail2ban/jail.d/nextcloud.local
[nextcloud]
backend = auto
enabled = true
port = 80,443
protocol = tcp
filter = nextcloud
maxretry = 3
bantime = 86400
findtime = 43200
logpath = /opt/nextcloud/data/nextcloud.log
vi /etc/fail2ban/jail.d/sshd.local
[DEFAULT]
bantime = 24h
ignoreip = aaa.bbb.ccc.ddd eee.fff.ggg.hhh

[sshd]
enabled = true
# Override the default global configuration
# for specific jail sshd
bantime = 1d
maxretry = 3

(fail2ban/config/action.d/mail-whois.conf at master · fail2ban/fail2ban · GitHub)

vi /etc/fail2ban/action.d/mail-whois.conf

# Fail2Ban configuration file
#
# Author: Cyril Jaquier
#
#

[INCLUDES]

before = mail-whois-common.conf

[Definition]

# bypass ban/unban for restored tickets
norestored = 1

# Option:  actionstart
# Notes.:  command executed on demand at the first ban (or at the start of Fail2Ban if actionstart_on_demand is set to false).
# Values:  CMD
#
actionstart = printf %%b "Hi,\n
              The jail <name> has been started successfully.\n
              Regards,\n
              Fail2Ban"|mail -E 'set escape' -s "[Fail2Ban] <name>: started on <fq-hostname>" <dest>

# Option:  actionstop
# Notes.:  command executed at the stop of jail (or at the end of Fail2Ban)
# Values:  CMD
#
actionstop = printf %%b "Hi,\n
             The jail <name> has been stopped.\n
             Regards,\n
             Fail2Ban"|mail -E 'set escape' -s "[Fail2Ban] <name>: stopped on <fq-hostname>" <dest>

# Option:  actioncheck
# Notes.:  command executed once before each actionban command
# Values:  CMD
#
actioncheck = 

# Option:  actionban
# Notes.:  command executed when banning an IP. Take care that the
#          command is executed with Fail2Ban user rights.
# Tags:    See jail.conf(5) man page
# Values:  CMD
#
actionban = printf %%b "Hi,\n
            The IP <ip> has just been banned by Fail2Ban after
            <failures> attempts against <name>.\n\n
            Here is more information about <ip> :\n
            `%(_whois_command)s`\n
            Regards,\n
            Fail2Ban"|mail -E 'set escape' -s "[Fail2Ban] <name>: banned <ip> from <fq-hostname>" <dest>

# Option:  actionunban
# Notes.:  command executed when unbanning an IP. Take care that the
#          command is executed with Fail2Ban user rights.
# Tags:    See jail.conf(5) man page
# Values:  CMD
#
actionunban = 

[Init]

# Default name of the chain
#
name = default

# Destination/Addressee of the mail
#
dest = root
cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
vi /etc/fail2ban/jail.local :set nu 186 263
mta = mail
action = %(action_mw)s
fail2ban-client -t

*SELinux is causing a series of problems:

  • with Fail2ban once again, these were all resolved in version Rockylinux/Almalinux 8, but they have reappeared in version 9!
  • s-nail*

work-around measure:

semanage permissive -a fail2ban_t
semanage permissive -a mail_home_t

A quick search with sealert yields some results, but recommendations are useless.

sealert -a /var/log/audit/audit.log
ausearch -c 'fail2ban-server' --raw | audit2allow -M my-fail2banserver
semodule -X 300 -i my-fail2banserver.pp
ausearch -c 'mail' --raw | audit2allow -M my-mail
semodule -X 300 -i my-mail.pp
systemctl enable fail2ban --now

fail2ban-client status
Status
|- Number of jail:      2
`- Jail list:   nextcloud, sshd

fail2ban-client status sshd
fail2ban-client status nextcloud

A detailed analysis would be preferable, but it seems too complex…

grep fail2ban-server  /var/log/audit/audit.log | audit2allow -m fail2ban-server
ps -eZ | grep fail2ban_t
sesearch --allow -s httpd_sys_rw_content_t | grep file
sesearch --allow -s fail2ban_t | grep search
sesearch --allow -s fail2ban_t | grep file

Fail2ban no longer works since kernel update 5.14.0-570.28.1.el9_6.x86_64 !!!

Service starts normally but blocks access to site

It might just be an access rights issue.
This has solved the problem for now: :slightly_smiling_face:

chmod 0664 /root/dead.letter
chmod +x /etc/rc.d/rc.local
systemctl enable fail2ban --now

Subsequently, the configuration of SSL for NGINX, Redis and PostgreSQL will be undertaken.

Discourse supports Markdown, so there’s no need to use a WYSIWYG editor. Any plain text editor will do. You just need to mark up :wink: your text with the appropriate Markdown syntax before pasting it in.

I had pre-formatted my post in vi but copying and pasting was a disaster, so I did it directly in the post! Thanks for your help.

konki