NextCloud with Docker

7 minute read

Previously I deployed a nextcloud container in digitalocean droplet for my note synchronization. This time I want use nextcloud to hold all my documents and pictures, in case I want to read some files or enjoy the good momories, but the droplet has limited disk space, for large capacity, I need to pay more, so I tend to install NC on my own home server, and make it accessible from outside.

Run nextcloud service with docker-compose

NextCloud has official docker image, with docker-compose, it’s pretty easy to bring it up, here is my docker-compose.yml:

version: '2'

volumes:
  nextcloud:
  db:

services:
  db:
    image: mariadb:${MARIADB_VERSION}
    restart: always
    command: --transaction-isolation=READ-COMMITTED --binlog-format=ROW
    volumes:
      - db:/var/lib/mysql
    environment:
      - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
      - MYSQL_PASSWORD=${MYSQL_PASSWORD}
      - MYSQL_DATABASE=${MYSQL_DATABASE}
      - MYSQL_USER=${MYSQL_USER}

  app:
    container_name: nextcloud
    image: nextcloud:${NC_VERSION}
    restart: always
    ports:
      - 80:80
    links:
      - db
    volumes:
      - nextcloud:/var/www/html
    environment:
      - MYSQL_PASSWORD=${MYSQL_PASSWORD}
      - MYSQL_DATABASE=${MYSQL_DATABASE}
      - MYSQL_USER=${MYSQL_USER}
      - MYSQL_HOST=db

Create .env in the same directory as docker-compose.yml:

cat << EOF >> .env
MARIADB_VERSION=10.7
NC_VERSION=23.0.2
MYSQL_ROOT_PASSWORD=root_password
MYSQL_PASSWORD=password
MYSQL_DATABASE=db_name
MYSQL_USER=db_user
EOF

After creating above two files, then do up to make it working

docker-compose up -d

Create admin account with:

docker exec -it -u www-data nextcloud php occ maintenance:install --admin-user=admin --admin-pass=admin

This may take a while, wait until the following message shows up:

Nextcloud was successfully installed

Now we can login and upload files or install apps with web UI, but I prefer using occ to install apps.

Install nextcloud apps

docker exec -it -u www-data nextcloud php occ app:install maps
docker exec -it -u www-data nextcloud php occ app:install metadata
docker exec -it -u www-data nextcloud php occ app:install notes
docker exec -it -u www-data nextcloud php occ app:install spreed
docker exec -it -u www-data nextcloud php occ app:install deck
docker exec -it -u www-data nextcloud php occ app:install circles
docker exec -it -u www-data nextcloud php occ app:install recognize
docker exec -it -u www-data nextcloud php occ app:install previewgenerator
docker exec -it -u www-data nextcloud php occ app:install calendar
docker exec -it -u www-data nextcloud php occ app:install contacts

All installed apps can be listed with:

docker exec -it -u www-data nextcloud php occ app:list

If installing app failed with this error:

Error: Could not download app maps

Try again later some time.

NextCloud Client

For Android, download from F-Droid.

For Desktop, download from NextCloud download page.

Copy files to NC

Upload all the files to NC can be tedious, the good thing is there is a quick way to do this.

In the above docker-compose.yml, we created two docker volumes, one for database, and another one is for data files:

docker volume ls
DRIVER    VOLUME NAME
local     nextcloud_db
local     nextcloud_nextcloud

Check the mount point of the volume with this:

docker volume inspect --format '{{ .Mountpoint }}' nextcloud_nextcloud
/var/lib/docker/volumes/nextcloud_nextcloud/_data

Our files lives under the data/${username}/files directory, these are the files of user admin:

sudo ls -l /var/lib/docker/volumes/nextcloud_nextcloud/_data/data/admin/files
total 17296
drwxr-xr-x 2 www-data www-data     4096 Apr  5 06:35  Documents
-rw-r--r-- 1 www-data www-data  3963036 Apr  5 06:35 'Nextcloud intro.mp4'
-rw-r--r-- 1 www-data www-data 12699932 Apr  5 06:35 'Nextcloud Manual.pdf'
-rw-r--r-- 1 www-data www-data    50598 Apr  5 06:35  Nextcloud.png
drwxr-xr-x 2 www-data www-data     4096 Apr  5 06:35  Photos
-rw-r--r-- 1 www-data www-data   976625 Apr  5 06:35 'Reasons to use Nextcloud.pdf'
drwxr-xr-x 2 www-data www-data     4096 Apr  5 06:35  Templates

Now create a normal user fudong with occ command:

docker exec -it -u www-data nextcloud bash -c 'export OC_PASS="2Um&j6FWBgDVqe"; \
                            php occ user:add \
                            --password-from-env \
                            --display-name="Fudong Bai" \
                            --group="users" fudong'
The user "fudong" was created successfully
Display name set to "Fudong Bai"
Created group "users"
User "fudong" added to group "users"

As for now, there is nothing under this new users directory:

sudo ls -l /var/lib/docker/volumes/nextcloud_nextcloud/_data/data/fudong/
total 0

The most of the files I want to host in NC is tons of photos and books, create directories for them and copy them to NC:

sudo mkdir -p /var/lib/docker/volumes/nextcloud_nextcloud/_data/data/fudong/files/{Photos,Books}
sudo chown www-data:www-data -R /var/lib/docker/volumes/nextcloud_nextcloud/_data/data/fudong/files/
sudo ls -l /var/lib/docker/volumes/nextcloud_nextcloud/_data/data/fudong/files/
total 8
drwxr-xr-x 2 www-data www-data 4096 Apr  5 07:37 Books
drwxr-xr-x 2 www-data www-data 4096 Apr  5 07:37 Photos
sudo cp ~/Pictures/* /var/lib/docker/volumes/nextcloud_nextcloud/_data/data/fudong/files/Photos/

The last step is let NC recognize those files previously copied, asls with occ command:

docker exec -it -u www-data nextcloud php occ files:scan --all
Starting scan for user 1 out of 2 (admin)
Starting scan for user 2 out of 2 (fudong)
+---------+-------+--------------+
| Folders | Files | Elapsed time |
+---------+-------+--------------+
| 12      | 63    | 00:00:04     |
+---------+-------+--------------+

Before head to web UI of NC, there one thing to do, add trusted_domains:

docker exec -it -u www-data nextcloud php occ config:system:set trusted_domains 1 --value=ip.add.re.ss

Backup & Restore NextCloud Volume

Back up:

docker-compose stop
docker run --rm --volumes-from nextcloud -v $(pwd):/backup ubuntu tar cvf /backup/data.tar /var/www/html/
docker run --rm --volumes-from nextcloud_db_1 -v $(pwd):/backup ubuntu tar cvf /backup/db.tar /var/lib/mysql/

Restore:

docker run --rm --volumes-from nextcloud -v $(pwd):/backup ubuntu tar xvf /backup/data.tar -C /
docker run --rm --volumes-from nextcloud_db_1 -v $(pwd):/backup ubuntu tar xvf /backup/db.tar -C /

Make NC accessible from outside

There are many options for this purpose, choosing from one of the depends on your special situation, if ISP provides a public IP, then this combination can be used to achieve this:

I have a public IP, but cannot be accessed from outside for unkonw reason, firewall probably, I am no expoert on this, after struggling at this for couple of days, I decided to give up, and find frp could be a better choice for me.

Using frp need a server with a public IP address, it has server and client side, download the latest version and copy to server:

latest=$(curl -sL https://api.github.com/repos/fatedier/frp/releases/latest | jq -r ".tag_name" | cut -c2-)
wget -c https://github.com/fatedier/frp/releases/download/v${latest}/frp_${latest}_linux_amd64.tar.gz

Configurations for server side:

cat << EOF >> frps.ini
[common]
bind_port = 7000

# Specify a UDP port for KCP.
kcp_bind_port = 7000

vhost_http_port = 8080
subdomain_host = example.com
EOF
./frps -c frps.ini

Configurations for client side:

cat << EOF >> frpc.ini
[common]
server_addr = ip_address_of_vps
server_port = 7000

[nextcloud]
type = http
local_ip = 192.168.15.9
local_port = 8081
subdomain = cloud
EOF
./frpc -c frpc.ini

If there is nothing wrong with both client and server side, then make them start on boot by adding them to system service.

server:

sudo cp systemd/frps.service /usr/lib/systemd/system/
sudo cp frps /usr/bin/frps
sudo mkdir /etc/frp/
sudo cp frps.ini /etc/frp
sudo systemctl enable frps
sudo systemctl start frps
sudo systemctl status frps

client:

sudo cp systemd/frpc.service /usr/lib/systemd/system/
sudo cp frpc /usr/bin/frpc
sudo mkdir /etc/frp/
sudo cp frpc.ini /etc/frp
sudo systemctl enable frpc
sudo systemctl start frpc
sudo systemctl status frpc

Last but not least, don’t forget to add our domain to trusted_domains:

docker exec -it -u www-data nextcloud php occ config:system:set trusted_domains 2 --value=cloud.example.com

Generate certificates with acme.sh

Previously, I create certificate for https with the help of certbot docker image, after I managed the DNS with cloudflare, this is not working any more, so I use [acme.sh][acme.sh] this time.

First things first, create cloudflare API token:

  1. Head to Cloudflare Dashboard
  2. Go to User Profile (, at top right corner)
  3. Click {} API Tokens

Another thing need to do before creating certificates is to check the configuration of the domain:

  1. Go to Websites >> baylabs.cc >> SSL/TSS
  2. Under Overview, select Flexible
  3. Under Edge Certificates, make sure the following:
    • Always Use HTTPS is enabled
    • HTTPS Strict Transport Security (HSTS) is On (Include subdomain: On, Preload: On)
    • Automatic HTTPS Rewrites is enabled
    • Do NOT Disable Universal SSL

I am not sure if all the above are mandatory or not, this is a working setup, I can only confirm the last one, disabling universal SSL make https not working at all.

Then install acme.sh:

curl https://get.acme.sh | sh -s email=fudongbai@gmail.com

This will install acme.sh to $HOME/.acme.sh, and create an alias for convenient, logout and then login to proceed:

export CF_Token="IONbaUtIkgGSVvJraj1PPL1NWPefSUOqfcB5NBUk"
acme.sh --issue --dns dns_cf --server letsencrypt -d baylabs.cc -d '*.baylabs.cc'

When the following messages come up, the certificates were successfully created:

[Mon 09 May 2022 06:13:55 PM BST] Your cert is in: /home/username/.acme.sh/baylabs.cc/baylabs.cc.cer
[Mon 09 May 2022 06:13:55 PM BST] Your cert key is in: /home/username/.acme.sh/baylabs.cc/baylabs.cc.key
[Mon 09 May 2022 06:13:55 PM BST] The intermediate CA cert is in: /home/username/.acme.sh/baylabs.cc/ca.cer
[Mon 09 May 2022 06:13:55 PM BST] And the full chain certs is there: /home/username/.acme.sh/baylabs.cc/fullchain.cer

Next, for frp server, add https port and restart frps service:

vhost_https_port = 4443

Another thing need to do in server side is to add the following rules:

sudo iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-port 4443

Now, create an entry for nextcloud in /etc/frp/frpc.ini like this:

[cloud-secure]
type = https
subdomain = cloud
plugin = https2http
plugin_local_addr = 127.0.0.1:8123
plugin_crt_path = /etc/frp/ssl/baylabs.cc.cer
plugin_key_path = /etc/frp/ssl/baylabs.cc.key
plugin_host_header_rewrite = 127.0.0.1
plugin_header_X-From-Where = frp

After restarting frpc service, https for nextcloud should be working.

Note, there is no need to add port map in docker-compose.yml.

Update:
certbot should be also working in case of using cloudflare, as I found certbot-dns-cloudflare plugin for this in its official github repository, refer to its documentation for how to use it.

Troubleshooting

This is actually not a issue related to this post, it’s about markdown parsing, when generating this post, jekyll complain about following syntax error and cause command docker volume inspect command incomplete:

    Liquid Warning: Liquid syntax error (line 129): [:dot, "."] is not a valid expression in "{{ .Mountpoint }}" in /Users/fdbai/levante/blog/fudongbai.gitlab.io/_posts/2022-03-06-nextcloud-with-docker.md

Here is the answer from ashmaroli:

Liquid parses all instances of {{, }}, {%, %} as Liquid constructs. To tell Liquid not to parse such instances, wrap the code inside Liquid’s raw blocks.