Nextcloud

Setup

docker-compose

MariaDB hosted on a Docker container

See also

  • Database configuration — Nextcloud latest Administration Manual latest documentation 1

  • Deploy Nextcloud with docker-compose, Traefik 2, PostgreSQL and Redis | by ismail yenigül | FAUN Publication 2

  • How to backup data from docker-compose MariaDB container using mysqldump - TechOverflow 3

  1. follow the Docker instructions

  2. create the usual jobs directories

    mkdir -p /home/jobs/scripts/by-user/root/docker/nextcloud
    cd /home/jobs/scripts/by-user/root/docker/nextcloud
    
  3. create a Docker compose file

    /home/jobs/scripts/by-user/root/docker/nextcloud/docker-compose.yml
     1# Either select the MariaDB or Postgres configurations.
     2
     3# MariaDB hosted on a Docker container.
     4version: '2'
     5
     6volumes:
     7  nextcloud:
     8  db:
     9  redis:
    10
    11services:
    12  # See
    13  # https://help.nextcloud.com/t/mariadb-upgrade-from-10-5-11-to-10-6-causes-internal-server-error/120585
    14  db:
    15    hostname: db
    16    image: mariadb:10.5.11
    17    restart: always
    18    command: --transaction-isolation=READ-COMMITTED --binlog-format=ROW
    19    volumes:
    20      - ./db:/var/lib/mysql
    21    environment:
    22      - MYSQL_ROOT_PASSWORD=${DATABASE_ROOT_PASSWORD}
    23      - MYSQL_PASSWORD=${DATABASE_PASSWORD}
    24      - MYSQL_DATABASE=nextcloud
    25      - MYSQL_USER=nextcloud
    26
    27  app:
    28    image: nextcloud:production
    29    restart: always
    30    ports:
    31      - 4005:80
    32    links:
    33      - db
    34      - redis
    35    volumes:
    36      - ${DATA_PATH}:/var/www/html/data
    37      - ./nextcloud:/var/www/html
    38    environment:
    39      - MYSQL_PASSWORD=${DATABASE_PASSWORD}
    40      - MYSQL_DATABASE=nextcloud
    41      - MYSQL_USER=nextcloud
    42      - MYSQL_HOST=db
    43      - APACHE_DISABLE_REWRITE_IP=1
    44      - TRUSTED_PROXIES=127.0.0.1
    45      - OVERWRITEHOST=${FQDN}
    46      - OVERWRITEPROTOCOL=https
    47
    48  redis:
    49    image: redis:6.2.5
    50    restart: always
    51    hostname: redis
    52    volumes:
    53      - redis:/var/lib/redis
    

    Note

    Replace DATABASE_ROOT_PASSWORD, DATABASE_PASSWORD, DATA_PATH and FQDN with appropriate values.

  4. create a Systemd unit file. See also the Docker compose services section

    /home/jobs/services/by-user/root/docker-compose.nextcloud.service
     1[Unit]
     2Requires=docker.service
     3Requires=network-online.target
     4After=docker.service
     5After=network-online.target
     6# Enable these for the "scalable" variant.
     7#   Requires=redis.service
     8#   Requires=redis-socket.service
     9#   After=redis.service
    10#   After=redis-socket.service
    11
    12[Service]
    13Type=simple
    14WorkingDirectory=/home/jobs/scripts/by-user/root/docker/nextcloud
    15
    16ExecStart=/usr/bin/docker-compose up --remove-orphans
    17ExecStop=/usr/bin/docker-compose down --remove-orphans
    18
    19Restart=always
    20
    21[Install]
    22WantedBy=multi-user.target
    
  5. fix the permissions

    chmod 700 /home/jobs/scripts/by-user/root/nextcloud
    chmod 700 -R /home/jobs/services/by-user/root
    
  6. run the deploy script

  7. modify the reverse proxy port of your webserver configuration with 4005

File scanner

This service is required.

  1. create another Systemd unit file that will enable nextcloud to periodically scan for new files if added by external sources

    /home/jobs/services/by-user/root/scan-files-nextcloud.service
     1[Unit]
     2Requires=docker-compose.nextcloud.service
     3Requires=network-online.target
     4After=docker-compose.nextcloud.service
     5After=network-online.target
     6
     7[Service]
     8Type=simple
     9WorkingDirectory=/home/jobs/scripts/by-user/root/docker/nextcloud
    10Restart=no
    11
    12# Scan all new files.
    13ExecStart=/usr/bin/bash -c '/usr/bin/docker-compose exec -T --user www-data app php occ files:scan --all'
    
  2. create a Systemd timer file

    /home/jobs/services/by-user/root/scan-files-nextcloud.timer
    1[Unit]
    2Description=Once every week scan-files-nextcloud
    3
    4[Timer]
    5OnCalendar=weekly
    6Persistent=true
    7
    8[Install]
    9WantedBy=timers.target
    
  3. run the deploy script

Crontab

This service is required.

See also

  • Background jobs — Nextcloud latest Administration Manual latest documentation 4

  1. create another Systemd unit file that will run tasks periodically

    /home/jobs/services/by-user/root/nextcloud-cron.service
     1[Unit]
     2Requires=docker-compose.nextcloud.service
     3Requires=network-online.target
     4After=docker-compose.nextcloud.service
     5After=network-online.target
     6
     7[Service]
     8Type=simple
     9WorkingDirectory=/home/jobs/scripts/by-user/root/docker/nextcloud
    10Restart=no
    11ExecStart=/usr/bin/bash -c '/usr/bin/docker-compose exec -T --user www-data app php cron.php'
    
  2. create a Systemd timer file

    /home/jobs/services/by-user/root/nextcloud-cron.timer
    1[Unit]
    2Description=Once every 15 minutes run Nextcloud's cron
    3
    4[Timer]
    5OnCalendar=*:0/15
    6Persistent=true
    7
    8[Install]
    9WantedBy=timers.target
    
  3. run the deploy script

Migration from MariaDB on a container container to PostgreSQL in host

  1. check that your PostgreSQL setup is fully configured and running

  2. add an entry in the pg_hba.conf file

    /etc/postgresql/13/main/pg_hba.conf
    host    nextclouddb   nextcloud   172.16.0.0/16  scram-sha-256
    
  3. change this setting in postgresql.conf

    /etc/postgresql/13/main/postgresql.conf
    listen_addresses = '*'
    
  4. restart Postgres

    systemctl restart postgresql.service
    
  5. stop the Nextcloud container

    systemctl stop docker-compose.nextcloud.service
    
  6. convert the database type. You will be prompted for the database password

    docker-compose exec --user www-data app php occ db:convert-type --all-apps pgsql ${DATABASE_USER} ${DATABASE_HOST} ${DATABASE_NAME}
    

    The Docker container must now comunicate with the PostgreSQL instance running on bare metal. The variables used here refer to the PostgreSQL instance.

  7. use this Docker compose file instead. See also the Docker compose services section

    /home/jobs/scripts/by-user/root/docker/nextcloud/docker-compose.yml
    56version: '2'
    57
    58volumes:
    59  nextcloud:
    60  redis:
    61
    62services:
    63  app:
    64    image: nextcloud:production
    65    restart: always
    66    ports:
    67      - 4005:80
    68    links:
    69      - redis
    70    volumes:
    71      - ${DATA_PATH}:/var/www/html/data
    72      - ./nextcloud:/var/www/html
    73    environment:
    74      - REDIS_HOST=redis
    75      - POSTGRES_DB=${DATABASE_NAME}
    76      - POSTGRES_USER=${DATABASE_USER}
    77      - POSTGRES_HOST=${DATABASE_HOST}
    78      - POSTGRES_PASSWORD=${DATABASE_PASSWORD}
    79      - APACHE_DISABLE_REWRITE_IP=1
    80      - TRUSTED_PROXIES=127.0.0.1
    81      - OVERWRITEHOST=${FQDN}
    82      - OVERWRITEPROTOCOL=https
    83
    84  redis:
    85    image: redis:6.2.5
    86    restart: always
    87    hostname: redis
    88    volumes:
    89      - redis:/var/lib/redis
    

    Note

    Replace DATABASE_NAME, DATABASE_USER, DATABASE_PASSWORD, DATABASE_HOST, DATA_PATH, FQDN with appropriate values. DATABASE_HOST may be different from FQDN if PostgreSQL is not reachable through Internet or is running on a different machine.

  8. run the deploy script

A more scalable setup

To have a more serious setup you must separate some of the components. In this example Nextcloud’s FPM image, the NGINX webserver and ClamAV (optional) will be run in Docker. Redis will accessed by Docker containers from the host machine.

See also

  • Access host socket from container - socat - IT Playground Blog 6

  1. follow the Docker instructions

  2. create the jobs directories

    mkdir -p /home/jobs/scripts/by-user/root/docker/nextcloud
    cd /home/jobs/scripts/by-user/root/docker/nextcloud
    
  3. create a Docker compose file

    /home/jobs/scripts/by-user/root/docker/nextcloud/docker-compose.yml
     92version: '2'
     93
     94volumes:
     95  app:
     96  web:
     97
     98services:
     99  web:
    100    image: nginx:1.23.1
    101    restart: always
    102    ports:
    103      - 127.0.0.1:4005:80
    104    links:
    105      - app
    106    volumes:
    107      - ./nginx.conf:/etc/nginx/nginx.conf
    108    volumes_from:
    109      - app
    110    networks:
    111      - mynetwork
    112
    113  app:
    114    image: nextcloud:24.0.5-fpm
    115    restart: always
    116    links:
    117      - clamav
    118    volumes:
    119      - ${DATA_PATH}:/var/www/html/data
    120      - ${HTML_DATA_PATH}:/var/www/html
    121      - ${PHP_CONFIG_DATA_PATH}:/usr/local/etc/php/conf.d
    122      - /var/run/redis.sock:/var/run/redis.sock
    123    environment:
    124      - TZ=Europe/Rome
    125      - REDIS_HOST=/var/run/redis.sock
    126      - POSTGRES_DB=${DATABASE_NAME}
    127      - POSTGRES_USER=${DATABASE_USER}
    128      - POSTGRES_HOST=${DATABASE_HOST}
    129      - POSTGRES_PASSWORD=${DATABASE_PASSWORD}
    130      - APACHE_DISABLE_REWRITE_IP=1
    131      - TRUSTED_PROXIES=127.0.0.1
    132      - OVERWRITEHOST=${FQDN}
    133      - OVERWRITEPROTOCOL=https
    134      - OVERWRITECLIURL=https://${FQDN}
    135      - PHP_UPLOAD_LIMIT=8G
    136      - PHP_MEMORY_LIMIT=2G
    137
    138  clamav:
    139    image: clamav/clamav:latest
    140    restart: always
    141    hostname: clamav
    

    Note

    Replace DATABASE_ROOT_PASSWORD, DATABASE_PASSWORD, DATA_PATH, HTML_DATA_PATH, PHP_CONFIG_DATA_PATH and FQDN with appropriate values.

  4. create a Systemd unit file. See also the Docker compose services section

    /home/jobs/services/by-user/root/docker-compose.nextcloud.service
     1[Unit]
     2Requires=docker.service
     3Requires=network-online.target
     4After=docker.service
     5After=network-online.target
     6# Enable these for the "scalable" variant.
     7#   Requires=redis.service
     8#   Requires=redis-socket.service
     9#   After=redis.service
    10#   After=redis-socket.service
    11
    12[Service]
    13Type=simple
    14WorkingDirectory=/home/jobs/scripts/by-user/root/docker/nextcloud
    15
    16ExecStart=/usr/bin/docker-compose up --remove-orphans
    17ExecStop=/usr/bin/docker-compose down --remove-orphans
    18
    19Restart=always
    20
    21[Install]
    22WantedBy=multi-user.target
    
  5. follow the Redis instructions

  6. create a copy of the Redis socket with specific permissions. Use this Systemd service. The Redis docket must have the same user id (in this case 33) of the nextcloud files in the Docker image.

    /home/jobs/services/by-user/root/redis-socket.service
     1[Unit]
     2Requires=network-online.target
     3Requires=redis.service
     4After=network-online.target
     5After=redis.service
     6
     7[Service]
     8Type=simple
     9ExecStart=/usr/bin/socat UNIX-LISTEN:/var/run/redis.sock,user=33,group=33,mode=0660,fork UNIX-CLIENT:/var/run/redis/redis-server.sock
    10Restart=always
    11
    12[Install]
    13WantedBy=multi-user.target
    
  7. copy the php data from the image to the PHP_CONFIG_DATA_PATH. First of all comment the PHP_CONFIG_DATA_PATH volume from the docker-compose file then run

    docker-compose up --remove orphans
    docker cp $(docker container ls | grep nextcloud:24.0.5-fpm | awk '{print $1}'):/usr/local/etc/php/conf.d "${PHP_CONFIG_DATA_PATH}"
    docker-compose down
    
  8. use this NGINX configuration file as example.

    /home/jobs/scripts/by-user/root/docker/nextcloud/nginx.conf
      1worker_processes auto;
      2
      3error_log  /var/log/nginx/error.log warn;
      4pid        /var/run/nginx.pid;
      5
      6
      7events {
      8    worker_connections  1024;
      9}
     10
     11
     12http {
     13    include       /etc/nginx/mime.types;
     14    default_type  application/octet-stream;
     15
     16    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
     17                      '$status $body_bytes_sent "$http_referer" '
     18                      '"$http_user_agent" "$http_x_forwarded_for"';
     19
     20    access_log  /var/log/nginx/access.log  main;
     21
     22    sendfile        on;
     23    #tcp_nopush     on;
     24
     25    # Prevent nginx HTTP Server Detection
     26    server_tokens   off;
     27
     28    keepalive_timeout  65;
     29
     30    #gzip  on;
     31
     32    upstream php-handler {
     33        server app:9000;
     34    }
     35
     36    server {
     37        server_name  ${FQDN}
     38        listen 80;
     39
     40        # HSTS settings
     41        # WARNING: Only add the preload option once you read about
     42        # the consequences in https://hstspreload.org/. This option
     43        # will add the domain to a hardcoded list that is shipped
     44        # in all major browsers and getting removed from this list
     45        # could take several months.
     46        add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;" always;
     47
     48        # set max upload size
     49        client_max_body_size 512M;
     50        fastcgi_buffers 64 4K;
     51
     52        # Enable gzip but do not remove ETag headers
     53        gzip on;
     54        gzip_vary on;
     55        gzip_comp_level 4;
     56        gzip_min_length 256;
     57        gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
     58        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;
     59
     60        # Pagespeed is not supported by Nextcloud, so if your server is built
     61        # with the `ngx_pagespeed` module, uncomment this line to disable it.
     62        #pagespeed off;
     63
     64        # HTTP response headers borrowed from Nextcloud `.htaccess`
     65        add_header Referrer-Policy                      "no-referrer"   always;
     66        add_header X-Content-Type-Options               "nosniff"       always;
     67        add_header X-Download-Options                   "noopen"        always;
     68        add_header X-Frame-Options                      "SAMEORIGIN"    always;
     69        add_header X-Permitted-Cross-Domain-Policies    "none"          always;
     70        add_header X-Robots-Tag                         "none"          always;
     71        add_header X-XSS-Protection                     "1; mode=block" always;
     72
     73        # Remove X-Powered-By, which is an information leak
     74        fastcgi_hide_header X-Powered-By;
     75
     76        # Path to the root of your installation
     77        root /var/www/html;
     78
     79        # Specify how to handle directories -- specifying `/index.php$request_uri`
     80        # here as the fallback means that Nginx always exhibits the desired behaviour
     81        # when a client requests a path that corresponds to a directory that exists
     82        # on the server. In particular, if that directory contains an index.php file,
     83        # that file is correctly served; if it doesn't, then the request is passed to
     84        # the front-end controller. This consistent behaviour means that we don't need
     85        # to specify custom rules for certain paths (e.g. images and other assets,
     86        # `/updater`, `/ocm-provider`, `/ocs-provider`), and thus
     87        # `try_files $uri $uri/ /index.php$request_uri`
     88        # always provides the desired behaviour.
     89        index index.php index.html /index.php$request_uri;
     90
     91        # Rule borrowed from `.htaccess` to handle Microsoft DAV clients
     92        location = / {
     93            if ( $http_user_agent ~ ^DavClnt ) {
     94                return 302 /remote.php/webdav/$is_args$args;
     95            }
     96        }
     97
     98        location = /robots.txt {
     99            allow all;
    100            log_not_found off;
    101            access_log off;
    102        }
    103
    104        # Make a regex exception for `/.well-known` so that clients can still
    105        # access it despite the existence of the regex rule
    106        # `location ~ /(\.|autotest|...)` which would otherwise handle requests
    107        # for `/.well-known`.
    108        location ^~ /.well-known {
    109            # The rules in this block are an adaptation of the rules
    110            # in `.htaccess` that concern `/.well-known`.
    111
    112            location = /.well-known/carddav { return 301  https://${FQDN}/remote.php/dav/; }
    113            location = /.well-known/caldav  { return 301  https://${FQDN}/remote.php/dav/; }
    114            location = /.well-known/webfinger  { return 301  https://${FQDN}/index.php/.well-known/webfinger; }
    115            location = /.well-known/nodeinfo  { return 301  https://${FQDN}/index.php/.well-known/nodeinfo; }
    116
    117            location /.well-known/acme-challenge    { try_files $uri $uri/ =404; }
    118            location /.well-known/pki-validation    { try_files $uri $uri/ =404; }
    119
    120            # Let Nextcloud's API for `/.well-known` URIs handle all other
    121            # requests by passing them to the front-end controller.
    122            return 301 /index.php$request_uri;
    123        }
    124
    125        # Rules borrowed from `.htaccess` to hide certain paths from clients
    126        location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)(?:$|/)  { return 404; }
    127        location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console)                { return 404; }
    128
    129        # Ensure this block, which passes PHP files to the PHP process, is above the blocks
    130        # which handle static assets (as seen below). If this block is not declared first,
    131        # then Nginx will encounter an infinite rewriting loop when it prepends `/index.php`
    132        # to the URI, resulting in a HTTP 500 error response.
    133        location ~ \.php(?:$|/) {
    134            # Required for legacy support
    135            rewrite ^/(?!index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|oc[ms]-provider\/.+|.+\/richdocumentscode\/proxy) /index.php$request_uri;
    136
    137            fastcgi_split_path_info ^(.+?\.php)(/.*)$;
    138            set $path_info $fastcgi_path_info;
    139
    140            try_files $fastcgi_script_name =404;
    141
    142            include fastcgi_params;
    143            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    144            fastcgi_param PATH_INFO $path_info;
    145            #fastcgi_param HTTPS on;
    146
    147            fastcgi_param modHeadersAvailable true;         # Avoid sending the security headers twice
    148            fastcgi_param front_controller_active true;     # Enable pretty urls
    149            fastcgi_pass php-handler;
    150
    151            fastcgi_intercept_errors on;
    152            fastcgi_request_buffering off;
    153        }
    154
    155        location ~ \.(?:css|js|svg|gif)$ {
    156            try_files $uri /index.php$request_uri;
    157            expires 6M;         # Cache-Control policy borrowed from `.htaccess`
    158            access_log off;     # Optional: Don't log access to assets
    159        }
    160
    161        location ~ \.woff2?$ {
    162            try_files $uri /index.php$request_uri;
    163            expires 7d;         # Cache-Control policy borrowed from `.htaccess`
    164            access_log off;     # Optional: Don't log access to assets
    165        }
    166
    167        # Rule borrowed from `.htaccess`
    168        location /remote {
    169            return 301 /remote.php$request_uri;
    170        }
    171
    172        location / {
    173            try_files $uri $uri/ /index.php$request_uri;
    174        }
    175    }
    176}
    

    Note

    Replace FQDN with appropriate value.

  9. edit the redis configuration in the PHP config (${HTML_DATA_PATH}/config/config.php) like this

    # [ ... ]
    
    'redis' =>
    array (
      'host' => '/var/run/redis.sock',
      'password' => '',
    ),
    
    # [ ... ]
    
  10. run the deploy script

  11. modify the reverse proxy port of your webserver configuration with 4005

  12. connect to your Nextcloud instance as admin and configure the antivirus app from the settings. Go to Settings -> Security -> Antivirus for Files

    • Mode: ClamAV Daemon

    • Host: clamav

    • Port: 3310

    • When infected files are found during a background scan: Delete file

Apps

Tips for Nextcloud apps

Talk

You can use metered.ca OpenRelay’s STUN and TURN servers to improve communication between WebRTC P2P clients.

See also

  • Free WebRTC TURN Server - Open Relay Project | Open Relay Project - Free WebRTC TURN Server 5

Footnotes

1

https://docs.nextcloud.com/server/20/admin_manual/configuration_database/linux_database_configuration.html CC BY 3.0, Copyright (c) Nextcloud contributors

2

https://faun.pub/deploy-nextcloud-with-docker-compose-traefik-2-postgresql-and-redis-fd1ffc166173 unknown license

3

https://techoverflow.net/2020/12/01/how-to-backup-data-from-docker-compose-mariadb-container-using-mysqldump/ unknown license

4

https://docs.nextcloud.com/server/latest/admin_manual/configuration_server/background_jobs_configuration.html CC-BY 3.0, Nextcloud contributors

5

https://www.metered.ca/tools/openrelay/#turn-server-for-nextcloud-talk unknown license

6

https://blog.it-playground.eu/accessing-host-socket-when-using-namespaces-for-docker-isolation/ unknown license