Skip to main content

Nextcloud - Docker Compose Stack, HTTPS, S3 Storage, LDAPS Active Directory Authentication, Maintenence & other Settings

2561 words·
Nextcloud AWS S3 IAM Docker-Compose rsync https Apache Certbot Let's Encrypt Active Directory LDAPS

The following tutorial sets up an NextCloud instance with Docker Compose and secures it withs https. I also mount an S3 Bucket as storage pool. This is very helpful especially when you host NextCloud on an EC2 instance, because storage on S3 Buckets is a lot cheaper then EBS volumes.

NextCloud: http version
#

Install Docker and add your user to the docker group:
sudo usermod -aG docker username
Logout and login again for the group settings to apply.

cd Move into Home directory
mkdir nextcloud Create a directory for nextcloud
cd nextcloud Move into nextcloud direcotry

Create a Docker Compose file docker-compose.yml and add the following script:

version: '2'

volumes:
  nextcloud:
  db:

services:
  db:
    image: mariadb:10.5
    container_name: nextcloud-mariadb
    restart: always
    command: --transaction-isolation=READ-COMMITTED --binlog-format=ROW
    volumes:
      - ./nextcloud-data/db:/var/lib/mysql
    environment:
      - MYSQL_ROOT_PASSWORD=
      - MYSQL_PASSWORD=
      - MYSQL_DATABASE=nextcloud
      - MYSQL_USER=nextcloud

  app:
    image: nextcloud
    container_name: nextcloud
    restart: always
    ports:
      - 8080:80
    links:
      - db
    volumes:
      - ./nextcloud-data/html:/var/www/html
      - ./nextcloud-data/data:/var/www/html/data
    environment:
      - MYSQL_PASSWORD=
      - MYSQL_DATABASE=nextcloud
      - MYSQL_USER=nextcloud
      - MYSQL_HOST=db

In this example port 8080 is used the access NextCloud.

The script differs from the offical NextCloud Compose file that it does not use Docker volumes, insted it maps the data of the NextCloud app and mariadb directly to the filesystem of the server. This makes it easier to access the data on the server.

Set passwords for the environment variables MYSQL_PASSWORD and MYSQL_ROOT_PASSWORD.

Create and start the containers with:
docker compose up -d or docker-compose up -d depending on your Docker version.

Open NextCloud in your brwoser with server_IP:8080 and login in with the following credentials:
Nextcloud user: ncadmin
Nextcloud password: nextcloud

Upload some files and stop the Docker containers with docker compose down or docker-compose down for older Docker versions and start the the containers again, to the check if the data is persistent.

Note: Since the data is not yet encrypted with https, you should only transmit sensible data when you NextCloud instance is on a local and not a public network.

NextCloud: https version
#

Let’s Encrypt
#

A fully qualified domain name (FQDN) that points to the public IP of your server is necessary to create a certificate with Let’s Encrypt

sudo apt update Update packet manager
sudo apt install certbot Install certbot
sudo certbot certonly --standalone -d nextcloud.jklug.work Create Certificates
It is necessary to create an - temporary - inbound firewall rule for port 80 to issue the Let’s Encrypt certificates.

Docker-Compose: https Version
#

The secure NextCloud with https it’s necessary to bind port 443 and make the /etc/apache2 container directory accessible from the host. It is also necessary to make the /etc/letsencrypt host directory accessible from within the container.

Copy the apache2 container directory to your host with the following command:
sudo docker cp nextcloud:/etc/apache2 /home/ubuntu/nextcloud/nextcloud-data/
Replace nextcloud with the name or ID of your NextCloud container.

Stop the containers with docker compose down and change your docker-compose.yml file as follows:

version: '2'

volumes:
  nextcloud:
  db:

services:
  db:
    image: mariadb:10.5
    container_name: nextcloud-mariadb
    restart: always
    command: --transaction-isolation=READ-COMMITTED --binlog-format=ROW
    volumes:
      - ./nextcloud-data/db:/var/lib/mysql
    environment:
      - MYSQL_ROOT_PASSWORD=
      - MYSQL_PASSWORD=
      - MYSQL_DATABASE=nextcloud
      - MYSQL_USER=nextcloud

  app:
    image: nextcloud
    container_name: nextcloud
    restart: always
    ports:
      - 8080:80
      - 443:443
    links:
      - db
    volumes:
      - ./nextcloud-data/html:/var/www/html
      - ./nextcloud-data/data:/var/www/html/data
      - ./nextcloud-data/apache2:/etc/apache2
      - /etc/letsencrypt/:/var/certs
    environment:
      - MYSQL_PASSWORD=
      - MYSQL_DATABASE=nextcloud
      - MYSQL_USER=nextcloud
      - MYSQL_HOST=db

Create and start the containers again with:
docker compose up -d or docker-compose up -d depending on your Docker version.

Apache: https config
#

Open the apache default configuration file:
/home/ubuntu/nextcloud/nextcloud-data/apache2/sites-available/000-default.conf

Here is backup of the default configuration in case you wan’t to change it back at a later point in time.

<VirtualHost *:80>
        # The ServerName directive sets the request scheme, hostname and port that
        # the server uses to identify itself. This is used when creating
        # redirection URLs. In the context of virtual hosts, the ServerName
        # specifies what hostname must appear in the request's Host: header to
        # match this virtual host. For the default virtual host (this file) this
        # value is not decisive as it is used as a last resort host regardless.
        # However, you must set it for any further virtual host explicitly.
        #ServerName www.example.com

        ServerAdmin webmaster@localhost
        DocumentRoot /var/www/html

        # Available loglevels: trace8, ..., trace1, debug, info, notice, warn,
        # error, crit, alert, emerg.
        # It is also possible to configure the loglevel for particular
        # modules, e.g.
        #LogLevel info ssl:warn

        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined

        # For most configuration files from conf-available/, which are
        # enabled or disabled at a global level, it is possible to
        # include a line for only one particular virtual host. For example the
        # following line enables the CGI configuration for this host only
        # after it has been globally disabled with "a2disconf".
        #Include conf-available/serve-cgi-bin.conf
</VirtualHost>

Replace the default configuration with the following configuration that redirects port 80 to port 443. Replace nextcloud.jklug.work with your domain name, also check if your path for the SSL certificates is correct:

<VirtualHost *:80>
        ServerName nextcloud.jklug.work # Change Domain Name
        RewriteEngine On
        RewriteCond %{HTTPS} !=on
        RewriteRule ^/?(.*) https://%{SERVER_NAME}/$1 [R=301,L]
        ServerAdmin webmaster@localhost
        DocumentRoot /var/www/html

        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
<VirtualHost *:443>

        ServerName nextcloud.jklug.work # Change Domain Name
        ServerAdmin webmaster@localhost
        DocumentRoot /var/www/html

        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined

        RequestHeader set X-Forwarded-Proto "https"
        SSLEngine On
        <IfModule mod_headers.c>
                Header always set Strict-Transport-Security "max-age=15768000; includeSubDomains; preload"
        </IfModule>

        SSLCertificateFile /var/certs/live/nextcloud.jklug.work/fullchain.pem # Change Domain / Path name
        SSLCertificateKeyFile /var/certs/live/nextcloud.jklug.work/privkey.pem # Change Domain / Path name
</VirtualHost>

NextCloud: config.php
#

Open ~/nextcloud/nextcloud-data/html/config/config.php and change your server IP and port IP:8080 to your domain name.

Original config:

<?php
$CONFIG = array (
  'htaccess.RewriteBase' => '/',
  'memcache.local' => '\\OC\\Memcache\\APCu',
  'apps_paths' =>
  array (
    0 =>
    array (
      'path' => '/var/www/html/apps',
      'url' => '/apps',
      'writable' => false,
    ),
    1 =>
    array (
      'path' => '/var/www/html/custom_apps',
      'url' => '/custom_apps',
      'writable' => true,
    ),
  ),
  'instanceid' => 'ocqen75ujvqw',
  'passwordsalt' => 'AkY44IZG8fGAu6wY++hCRcTvfTpwXx',
  'secret' => '9q4xADbZK1Snh2rPAbUbUAvuuGSm6JPYdeIrtQPU1WJ0Rbv5',
  'trusted_domains' =>
  array (
    0 => '3.70.156.13:8080', # Change to domain name
  ),
  'datadirectory' => '/var/www/html/data',
  'dbtype' => 'mysql',
  'version' => '27.0.2.1',
  'overwrite.cli.url' => 'http://3.70.156.13:8080', # Change to domain name
  'dbname' => 'nextcloud',
  'dbhost' => 'db',
  'dbport' => '',
  'dbtableprefix' => 'oc_',
  'mysql.utf8mb4' => true,
  'dbuser' => 'nextcloud',
  'dbpassword' => 'x25K6CCk4Bk8u4DN6rN28sEvDkm',
  'installed' => true,
);

It should look like in this example:

<?php
$CONFIG = array (
  'htaccess.RewriteBase' => '/',
  'memcache.local' => '\\OC\\Memcache\\APCu',
  'apps_paths' =>
  array (
    0 =>
    array (
      'path' => '/var/www/html/apps',
      'url' => '/apps',
      'writable' => false,
    ),
    1 =>
    array (
      'path' => '/var/www/html/custom_apps',
      'url' => '/custom_apps',
      'writable' => true,
    ),
  ),
  'instanceid' => 'ocqen75ujvqw',
  'passwordsalt' => 'AkY44IZG8fGAu6wY++hCRcTvfTpwXx',
  'secret' => '9q4xADbZK1Snh2rPAbUbUAvuuGSm6JPYdeIrtQPU1WJ0Rbv5',
  'trusted_domains' =>
  array (
    0 => 'nextcloud.jklug.work', # Change to domain name
  ),
  'datadirectory' => '/var/www/html/data',
  'dbtype' => 'mysql',
  'version' => '27.0.2.1',
  'overwrite.cli.url' => 'https://nextcloud.jklug.work', # Change to domain name
  'dbname' => 'nextcloud',
  'dbhost' => 'db',
  'dbport' => '',
  'dbtableprefix' => 'oc_',
  'mysql.utf8mb4' => true,
  'dbuser' => 'nextcloud',
  'dbpassword' => 'x25K6CCk4Bk8u4DN6rN28sEvDkm',
  'installed' => true,
);

Apache: Enable SSL and reload apache service
#

Execute the internal terminal of your nextcloud container with the following command:
docker exec -it nextcloud bash

Enable the apache SSL module: a2enmod ssl
Restart the apache service: /etc/init.d/apache2 reload or service apache2 restart

Open your domain in a browser to check if the connection is secure.

Mount a S3 Bucket to NextCloud
#

It is not possible to mount an already existing S3 Bucket to NextCloud, therefore it is necessary to create and use an IAM user and a IAM permissions that allows NextCloud to create a S3 Bucket.

Create IAM user and permission
#

Create a new IAM user:

Create an access key and secret key for the new user and copy them:

The keys should look as follows:
Access key: AKIARCHUALINQ5EKE5HN
Secret access key: 762kFaMJKGGyyYFQJxIFCQX3fv+bO6EPkd/D6fD6

The following IAM permission allows to create S3 Buckets. Create the permission and attach it to your IAM user

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "s3:ListStorageLensConfigurations",
                "s3:ListAccessPointsForObjectLambda",
                "s3:GetAccessPoint",
                "s3:PutAccountPublicAccessBlock",
                "s3:GetAccountPublicAccessBlock",
                "s3:ListAllMyBuckets",
                "s3:ListAccessPoints",
                "s3:PutAccessPointPublicAccessBlock",
                "s3:ListJobs",
                "s3:PutStorageLensConfiguration",
                "s3:ListMultiRegionAccessPoints",
                "s3:CreateJob"
            ],
            "Resource": "*"
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": "s3:*",
            "Resource": "arn:aws:s3:::*"
        }
    ]
}

After the S3 Bucket was created, change the IAM permission to access only the specific S3 Bucket you have created.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowBucketAccess",
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:GetObject",
                "s3:DeleteObject",
                "s3:ListBucket"
            ],
            "Resource": [
                "arn:aws:s3:::jklug-nextcloud/*",
                "arn:aws:s3:::jklug-nextcloud"
            ]
        }
    ]
}

NextCloud External Storage
#

Go to the App section on NextCloud, search for “external storage” and enable it:

Go to Administration / External Storage and choose “Amazon S3”:

Create a new S3 Bucket:

After the new S3 Bucket is created you can acces it from the “Files” section:

S3 : Block public access
#

After the creation of the S3 Bucket it is necessary to block the public access for the bucket. Open your S3 Bucket and go to permissions / Block public acces and click edit:

Check Block public access and save the changes:


Active Directory Integration
#

LDAP
#

  • Use port 389
  • Select Active Directory group that is allowed to authenticate via NextCloud

LDAPS
#

For a LDAPS connection it’s necessary to export the Root CA from the Windows Server, copy it into the Nextcloud Docker Compose directory, change the docker-compose.yml file and add a Dockerfile.

# Folder / file structure
~/nextcloud/docker-compose.yml
~/nextcloud/build/Dockerfile
~/nextcloud/build/jklug-WIN2022-1-CA.crt
~/nextcloud/nextcloud-data

docker-compose.yml

  • Add the Domain Controller as DNS server
  • Remove the Nextcloud image and add a build command for a Dockerfile
version: '2'

volumes:
  nextcloud:
  db:

services:
  db:
    image: mariadb:10.5
    container_name: nextcloud-mariadb
    restart: always
    command: --transaction-isolation=READ-COMMITTED --binlog-format=ROW
    volumes:
      - ./nextcloud-data/db:/var/lib/mysql
    environment:
      - MYSQL_ROOT_PASSWORD=
      - MYSQL_PASSWORD=
      - MYSQL_DATABASE=nextcloud
      - MYSQL_USER=nextcloud

   app:
    container_name: nextcloud
    build:
        ./build # Build Dockerfile
    dns:
      - 192.168.70.2 # Add DC as DNS server
    restart: always
    ports:
      - 8080:80
      - 443:443
    links:
      - db
    volumes:
      - ./nextcloud-data/html:/var/www/html
      - ./nextcloud-data/data:/var/www/html/data
      - ./nextcloud-data/apache2:/etc/apache2
      - /etc/letsencrypt/:/var/certs
    environment:
      - MYSQL_PASSWORD=
      - MYSQL_DATABASE=nextcloud
      - MYSQL_USER=nextcloud
      - MYSQL_HOST=db

Dockerfile

FROM nextcloud:27.1.3-apache

COPY jklug-WIN2022-1-CA.crt /usr/share/ca-certificates/domaincert/jklug-WIN2022-1-CA.crt
RUN echo "domaincert/jklug-WIN2022-1-CA.crt" >> /etc/ca-certificates.conf && update-ca-certificates

Explanation:

# Define Nextcloud Image
FROM nextcloud:27.1.3-apache

# Copy Windows Server Certification Authority Root CA into container
COPY jklug-WIN2022-1-CA.crt /usr/share/ca-certificates/domaincert/jklug-WIN2022-1-CA.crt
# Enable certificate
RUN echo "domaincert/jklug-WIN2022-1-CA.crt" >> /etc/ca-certificates.conf && update-ca-certificates

Build and start container

# Build image
docker compose build

# Build & start container
docker compose up -d

Enable LDAPS

  • Use ldaps://DNS-name instead of the IP
  • Change port to 636

OCC LDAP Settings
#

# Exec nextcloud container internal terminal
docker exec -it -u www-data nextcloud bash
# List LDAP configuration details: All LDAP Server
php occ ldap:show-config

# List LDAP configuration details: Single Server configuration
php occ ldap:show-config s01


# Delete existing LDAP configuration
php occ ldap:delete s01

Administrative tasks
#

Cron for maintenance Background Jobs
#

Open the Administration / Basic settings panel and activate cron background jobs:

Create a cron job and add the following command:

*/5 * * * * docker exec --user www-data nextcloud php -f cron.php >/dev/null 2>&1

Nextcloud Upgrade
#

Link: https://hub.docker.com/_/nextcloud/

  app:
    image: nextcloud:27.1.2-apache # Define image tag
    container_name: nextcloud
# Stop Docker stack
docker compose down

# Pull new image
docker compose pull

# Start Docker stack
docker compose up -d

Sometimes it’s neccessary to manually trigger the upgrade after the new image was pulled:

# Exec container internal terminal as user www-data
docker exec -it -u 33 nextcloud bash

# Upgrade nextcloud
./occ upgrade

Maintenance Mode
#

For some database upgrades it could be necessary to enable the maintenance mode first:

# Start / stop maintenance mode
./occ maintenance:mode --on
./occ maintenance:mode --off

Mail Notification
#

Per default mail notification are only sent hourly, the speed up the notification time you can trigger it with a cron job:

*/5 * * * * docker exec --user www-data nextcloud php occ activity:send-mails

Session Logout Time
#

# Define automatic session logout time: 900 sec / 15 min
docker exec --user www-data nextcloud php occ config:system:set session_lifetime --value=900
docker exec --user www-data nextcloud php occ config:system:set remember_login_cookie_lifetime --value=0
docker exec --user www-data nextcloud php occ config:system:set session_keepalive --value=false
docker exec --user www-data nextcloud php occ config:system:set auto_logout --value=true
# Check session logout time settings
docker exec --user www-data nextcloud php occ config:system:get session_lifetime
docker exec --user www-data nextcloud php occ config:system:get remember_login_cookie_lifetime
docker exec --user www-data nextcloud php occ config:system:get session_keepalive
docker exec --user www-data nextcloud php occ config:system:get auto_logout

Export user and group lists
#

In NextCloud environments with lots of users and groups it can be helpful to export them into lists.

Export all NextCloud users into a list:
docker exec --user www-data nextcloud php occ user:list > nextcloud-users.txt
Export all NextCloud groups into a list:
docker exec --user www-data nextcloud php occ group:list > nextcloud-groups.txt


Add files on server side
#

In case files are added directly from the filesystem on the server and not the nextcloud interface it is necessary to change the file owner and trigger an occ resan.

Go to the files folder of the user where you added the files:
/home/ubuntu/nextcloud/nextcloud/data/ncadmin/files

Change user and group owner to www-data:
chown -R www-data:www-data file_or_folder

Trigger a occ rescan:
docker exec --user www-data nextcloud php occ files:scan --all


Migrate NextCloud
#

It’s quite easy to migrate NextCloud to another server. The following steps will help you to do so.

  • Make sure Docker is installed on the new server and the new user has Docker permissions.

  • Stop the Docker-Stack docker compose down

  • Use Rsync to copy the data with it’s permissions to the new server:
    rsync -azP -e ssh /home/ubuntu/nextcloud user@IP:/home/ubuntu/

  • Open the config.php file, in this example it’s in the following directory: ~/nextcloud/nextcloud-data/html/config/config.php

Change the IP to the IP or domain name of your new server

# Change IP to new server IP
array (
    0 => 'new_IP:8080',
# Or add another entry for the IP of your new server
array (
    0 => 'old_IP:8080',
    1 => 'new_IP:8080', # New server
 'overwrite.cli.url' => 'new_IP',

Apache2 SSL & TLS Settings
#

<VirtualHost *:80>
        ServerName nextcloud.jklug.work # Change Domain Name
        RewriteEngine On
        RewriteCond %{HTTPS} !=on
        RewriteRule ^/?(.*) https://%{SERVER_NAME}/$1 [R=301,L]
        ServerAdmin webmaster@localhost
        DocumentRoot /var/www/html

        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
<VirtualHost *:443>

        ServerName nextcloud.jklug.work # Change Domain Name
        ServerAdmin webmaster@localhost
        DocumentRoot /var/www/html

        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined

        RequestHeader set X-Forwarded-Proto "https"
        SSLEngine On
        <IfModule mod_headers.c>
                Header always set Strict-Transport-Security "max-age=15768000; includeSubDomains; preload"
        </IfModule>

        # Allow only TLS version 1.2 & 1.3
        SSLProtocol -all +TLSv1.2 +TLSv1.3

        # Define cipher suites for SSL/TLS handshake
        SSLCipherSuite "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:DHE-RSA-AES256-GCM-SHA384"


        SSLCertificateFile /var/certs/live/nextcloud.jklug.work/fullchain.pem # Change Domain / Path name
        SSLCertificateKeyFile /var/certs/live/nextcloud.jklug.work/privkey.pem # Change Domain / Path name
</VirtualHost>

Troubleshooting
#

In case you decide to recover or migration only certain users from the data directory, it is necessary to also copy the .ocdata file. Regard the user and group ownership:
-rw-rwxr– 1 www-data www-data 0 Nov 15 11:30 .ocdata


Troubleshooting
#

Reverse proxy header configuration
#

“The reverse proxy header configuration is incorrect, or you are accessing Nextcloud from a trusted proxy”

  • Open the config.php file, in this example it’s in the following directory: ~/nextcloud/nextcloud-data/html/config/config.php
# Add the following line to config.php
'trusted_proxies' => array('localhost'),


# Should look like this
array (
    0 => 'yourdomain.com',
  ),
  'trusted_proxies' => array('localhost'),
  'datadirectory' => '/var/www/nextcloud/data',

Mount NextCloud WebDAV share to Linux host
#

sudo apt update Update package manager
apt install davfs2 Install WebDAV filesystem driver
mkdir /mountpoint Create directory to mount NextCloud share
vi /etc/davfs2/secrets Enter credentials for nextcloud user
# /etc/davfs2/secrets
/mountpoint   username  password
# For Example:
/mountpoint   ncadmin   nextcloud

Open fstab:
vi /etc/fstab

Add WebDAV entry to fstab:
https://nextcloud.jklug.work/remote.php/dav/files/ncadmin/ /mountpoint davfs user,rw,auto 0 0

Mount NextCloud WebDAV share:
mount /mountpoint