NextCloud with Docker
We are going to create a PostgreSQL container, a NextCloud container
(for the domain nc2.user1.fs.al
), and a network where these two will
be connected. We are going to use the existing nginx
as a reverse
proxy for this site.
1. Create a network
docker network ls
docker network create ncnet
docker network ls
docker network inspect ncnet | less
2. Create a PostgreSQL container
-
We can download and use the official postgres image:
docker search postgres | less
docker pull postgres:16
docker image ls
docker image history postgres:16 | less -
Let's create a directory for this container:
mkdir -p /var/docker/pg1
cd /var/docker/pg1/ -
Create a container:
cat <<'_EOF_' > create.sh
#!/bin/bash -x
passwd='pass123'
mkdir -p data
docker create \
--name pg1 \
--hostname postgresql \
--network ncnet \
--network-alias pg1 \
--mount type=bind,source=$(pwd)/data,destination=/var/lib/postgresql/data \
--restart unless-stopped \
--env POSTGRES_PASSWORD="$passwd" \
postgres:16
_EOF_nano create.sh
chmod +x create.sh
./create.sh
docker ps -a -
Start and test the container:
docker start pg1
docker ps
docker logs pg1 -f
ls data/docker exec -it pg1 bash
whoami
exit
docker exec -it -u postgres pg1 psql
\x
\l
\q -
Create a small script for running
psql
:cat <<'_EOF_' > psql.sh
#!/bin/bash
docker exec -it -u postgres pg1 psql "$@"
_EOF_nano psql.sh
chmod +x psql.sh
./psql.sh -c 'select 1;'
./psql.sh
\q
3. Install NextCloud
Let's create a directory for it:
mkdir -p /var/docker/nc2
cd /var/docker/nc2/
3.1 Create database and user
-
Create
settings.sh
with DB details:cat <<'_EOF_' > settings.sh
DB_HOST="pg1"
DB_NAME="nc2db"
DB_USER="nc2user"
DB_PASS="nc2pass"
_EOF_ -
Create script
db-init.sh
:cat <<'_EOF_' > db-init.sh
#!/bin/bash -x
source settings.sh
psql="docker exec -i -u postgres $DB_HOST psql"
cat <<EOF | tee /dev/tty | $psql
create database $DB_NAME template template0 encoding 'UTF8';
create user $DB_USER with password '$DB_PASS' createdb;
alter database $DB_NAME owner to $DB_USER;
grant all privileges on database $DB_NAME to $DB_USER;
grant all privileges on schema public to $DB_USER;
EOF
_EOF_ -
Run the script and create the database and user:
nano db-init.sh
chmod +x db-init.sh
./db-init.sh
../pg1/psql.sh -x -c "\l nc2db"
../pg1/psql.sh -x -c 'select * from pg_user'
3.2 Create a NextCloud container
-
Pull the nextcloud image:
docker search nextcloud | less
docker pull nextcloud
docker image ls
docker image history nextcloud | lessThis image includes Apache2, PHP and the NC application files.
-
Create a bash script for creating the container:
cat <<'_EOF_' > create.sh
#!/bin/bash -x
source settings.sh
mkdir -p www
docker create \
--name nc2 \
--hostname nc2 \
--network ncnet \
--publish 127.0.0.1:8085:80 \
--mount type=bind,source=$(pwd)/www,destination=/var/www/html \
\
--env POSTGRES_HOST=$DB_HOST \
--env POSTGRES_DB=$DB_NAME \
--env POSTGRES_USER=$DB_USER \
--env POSTGRES_PASSWORD="$DB_PASS" \
\
--env NEXTCLOUD_ADMIN_USER=$ADMIN_USER \
--env NEXTCLOUD_ADMIN_PASSWORD="$ADMIN_PASS" \
\
--env PHP_UPLOAD_LIMIT=$MAX_UPLOAD \
--env PHP_MEMORY_LIMIT=$MAX_UPLOAD \
--env APACHE_BODY_LIMIT=0 \
\
--env REDIS_HOST=redis1 \
--env NEXTCLOUD_TRUSTED_DOMAINS=$DOMAIN \
\
--restart unless-stopped \
nextcloud:latest
# create a redis container as well, on the same network
docker create \
--name redis1 \
--network ncnet \
--restart unless-stopped \
redis:latest
_EOF_nano create.sh
-
Append
ADMIN_USER
,ADMIN_PASS
,MAX_UPLOAD
andDOMAIN
tosettings.sh
:apt install pwgen
cat <<_EOF_ >> settings.sh
ADMIN_USER=admin
ADMIN_PASS=$(pwgen 15 1)
MAX_UPLOAD=1024M
DOMAIN="nc2.user1.fs.al"
_EOF_
nano settings.sh -
Run
create.sh
and start the containers:chmod +x create.sh
./create.sh
docker ps -a
docker start redis1
docker logs redis1 -f
docker start nc2
docker logs nc2 -f
ls www/
ls -al www/
ls www/config/
nano www/config/config.php
3.3 Setup reverse-proxy
-
Create a new site on the reverse proxy:
cd /etc/nginx/sites-available/
cp wp4 nc2
sed -i nc2 -e 's/wp4/nc2/g'
nano nc2 -
Edit
nc2
and make sure that theproxy_pass
line looks like this:proxy_pass http://127.0.0.1:8085;
Above
proxy_pass
add this line:client_max_body_size 1024M;
Also, remove aliases
www.nc2.user1.fs.al
. -
Enable this site:
cd ../sites-enabled/
ln -s ../sites-available/nc2 .
nginx -t -
Get an SSL cert and reload
nginx
:domain=nc2.user1.fs.al
email=dashohoxha@gmail.com
certbot certonly --webroot -w /var/www -d $domain --dry-run
certbot certonly --webroot -w /var/www \
-d $domain -m $email --agree-tos
certbot certificates | less -
Reload
nginx
:nginx -t
systemctl reload nginx -
Open in browser https://nc2.user1.fs.al and complete the installation wizard.
3.4 Improvements
-
Create the script
occ.sh
:cd /var/docker/nc2/
cat << '_EOF_' > occ.sh
#!/bin/bash
docker exec -it --user www-data nc2 php occ "$@"
_EOF_
chmod +x occ.sh
./occ.sh -
Let's fix some problems reported at https://nc2.user1.fs.al/settings/admin/overview
cat << '_EOF_' > config.sh
#!/bin/bash -x
./occ.sh config:system:set overwriteprotocol --value=https
./occ.sh config:system:set default_phone_region --value=AL
./occ.sh config:system:set maintenance_window_start --value=1 --type=integer
./occ.sh maintenance:repair --include-expensive
./occ.sh db:add-missing-indices
_EOF_nano config.sh
chmod +x config.sh
./config.sh -
To enable HSTS, edit
/etc/nginx/sites-enabled/nc2
and add a line like this:add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload" always;
Then reload
nginx
, -
To run cron jobs, edit crontab with
crontab -e
and append this line:*/5 * * * * docker exec -u www-data nc2 php /var/www/html/cron.php
4. Maintenance
4.1 Backup
-
Create a backup script:
cat << '_EOF_' > ~/nc2-backup.sh
#!/bin/bash -x
occ="docker exec -it --user www-data nc2 php occ"
pg_dump="docker exec -it -u postgres pg1 pg_dump"
$occ maintenance:mode --on
cd /var/docker/nc2/
$pg_dump --clean -d nc2db > db.sql
cd ..
tar cfz nc2-$(date +%Y%m%d).tgz nc2/
mv *.tgz ~/
$occ maintenance:mode --off
_EOF_ -
Test it:
nano ~/nc2-backup.sh
chmod +x ~/nc2-backup.sh
~/nc2-backup.sh
ls -lh ~/
tar tvfz ~/nc2-20250210.tgz | less
4.2 Restore
-
Uninstall NC2:
cat <<'_EOF_' > ~/nc2-remove.sh
#!/bin/bash -x
docker stop nc2
docker rm nc2
echo "drop database nc2db;" \
| docker exec -i -u postgres pg1 psql
rm -rf /var/docker/nc2/
_EOF_nano ~/nc2-remove.sh
chmod +x ~/nc2-remove.sh
~/nc2-remove.sh -
Restore from backup:
cat << '_EOF_' > ~/nc2-restore.sh
#!/bin/bash -x
[[ -z $1 ]] \
&& echo "Usage: $0 <backup-file.tgz>" >&2 \
&& exit 1
[[ ! -f $1 ]] \
&& echo "Error: File '$1' does not exist." >&2 \
&& exit 2
backup_file=$(realpath $1)
systemctl stop nginx
cd /var/docker/
tar xfz $backup_file
cd nc2/
source settings.sh
# recreate and restore the database
psql="docker exec -i -u postgres $DB_HOST psql"
echo "drop database $DB_NAME;" | $psql
./db-init.sh
cat db.sql | $psql -d $DB_NAME
# recreate and start the nextcloud and redis containers
./create.sh
docker start redis1
docker start nc2
./occ.sh maintenance:mode --off
systemctl start nginx
_EOF_nano ~/nc2-restore.sh
chmod +x ~/nc2-restore.sh
~/nc2-restore.sh
~/nc2-restore.sh ~/nc2-20250210.tgz
4.3 Update
cd /var/docker/nc2/
cat << '_EOF_' > update.sh
#!/bin/bash -x
docker pull nextcloud
docker pull redis
docker stop nc2
docker rm nc2
docker stop redis1
docker rm redis1
./create.sh
docker start redis1
docker start nc2
_EOF_
nano update.sh
chmod +x update.sh
./update.sh
4.4 Upgrade
cat << '_EOF_' > upgrade.sh
#!/bin/bash -x
cd /var/docker/nc2/
systemctl stop nginx
./occ.sh upgrade
./occ.sh app:update --all
./occ.sh db:add-missing-indices
./occ.sh db:convert-filecache-bigint
systemctl start nginx
_EOF_
nano upgrade.sh
chmod +x upgrade.sh
./upgrade.sh