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.
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 |
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