diff --git a/.docker/app/Dockerfile b/.docker/app/Dockerfile index dd10bab..d670249 100644 --- a/.docker/app/Dockerfile +++ b/.docker/app/Dockerfile @@ -5,6 +5,7 @@ FROM nextcloud:${NEXTCLOUD_VERSION} RUN apt-get update \ && apt-get install -y \ locales \ + postgresql-client \ poppler-utils \ && sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen \ && locale-gen \ diff --git a/.env.example b/.env.example index 3cd1bd4..1409f29 100644 --- a/.env.example +++ b/.env.example @@ -8,6 +8,8 @@ LETSENCRYPT_EMAIL= TZ= POSTGRES_PASSWORD= +NEXTCLOUD_BACKUP_DIR=/backups +NEXTCLOUD_UPGRADE_MIN_FREE_MB=2048 NEXTCLOUD_ADMIN_USER= NEXTCLOUD_ADMIN_PASSWORD= diff --git a/.gitignore b/.gitignore index 9f2e646..da756bf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /volumes +/backups .env docker-compose.override.yml diff --git a/README.md b/README.md index 6f71b1e..6648d0b 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ Languages avaliable: [pt-BR](docs/README_ptBR.md) - [After setup](#after-setup) - [Custom setup](#custom-setup) - [Customize docker-compose content](#customize-docker-compose-content) + - [Nextcloud upgrade hooks](#nextcloud-upgrade-hooks) - [PHP](#php) - [Run Nextcloud](#run-nextcloud) - [Use a specific version of Nextcloud](#use-a-specific-version-of-nextcloud) @@ -106,6 +107,46 @@ docker compose exec -u www-data app ./occ db:convert-filecache-bigint You can do this using environments and creating a file called `docker-compose.override.yml` to add new services. +### Redis + +The main compose files now include a `redis` service by default. This keeps the stack self-contained for Nextcloud installations that already use Redis in `config.php` and avoids depending on a host-specific external network. + +### Nextcloud upgrade hooks + +This repository mounts the official Nextcloud Docker hook directories so you can extend install and upgrade flows without touching the image entrypoint. + +The `app` service uses these mounts: + +```yaml +services: + app: + volumes: + - ./volumes/nextcloud:/var/www/html + - ./backups:/backups + - ./app-hooks/pre-installation:/docker-entrypoint-hooks.d/pre-installation + - ./app-hooks/post-installation:/docker-entrypoint-hooks.d/post-installation + - ./app-hooks/pre-upgrade:/docker-entrypoint-hooks.d/pre-upgrade + - ./app-hooks/post-upgrade:/docker-entrypoint-hooks.d/post-upgrade + - ./app-hooks/before-starting:/docker-entrypoint-hooks.d/before-starting +``` + +The upgrade hooks behave like this: + +- `pre-upgrade`: turns maintenance mode on +- `pre-upgrade`: saves the active app list to `/backups/app_list.old` +- `pre-upgrade`: checks free disk space on the Nextcloud volume and the backup volume +- `pre-upgrade`: creates a compressed PostgreSQL dump at `/backups` +- `post-upgrade`: saves the new app list to `/backups/app_list.new` and prints a diff when possible +- `post-upgrade`: runs the extra `occ` commands needed after a major upgrade +- `post-upgrade`: turns maintenance mode off at the end + +The following variables control the safety check and backup location: + +- `NEXTCLOUD_BACKUP_DIR`, defaulting to `/backups` +- `NEXTCLOUD_UPGRADE_MIN_FREE_MB`, defaulting to `2048` + +The `./backups` directory on the host must be writable by `www-data` inside the container. The recommended host-side ownership is `www-data:www-data` with mode `0755`. + ### Garage S3 primary storage Use `docker-compose-garages3.yml` when you want Nextcloud to store files in a Garage S3 bucket instead of the local `data/` directory. diff --git a/app-hooks/before-starting/.gitkeep b/app-hooks/before-starting/.gitkeep new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/app-hooks/before-starting/.gitkeep @@ -0,0 +1 @@ + diff --git a/app-hooks/post-upgrade/01-run-post-upgrade-commands.sh b/app-hooks/post-upgrade/01-run-post-upgrade-commands.sh new file mode 100755 index 0000000..4c35dcd --- /dev/null +++ b/app-hooks/post-upgrade/01-run-post-upgrade-commands.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +set -euo pipefail + +run_occ() { + echo "Running: php occ $*" + php occ "$@" +} + +echo "Running post-upgrade Nextcloud commands" +php occ app:list > /backups/app_list.new +if [ -f /backups/app_list.old ]; then + echo "Comparing app lists" + diff -u /backups/app_list.old /backups/app_list.new || true +fi +run_occ db:add-missing-columns +run_occ db:add-missing-indices +run_occ db:add-missing-primary-keys +run_occ maintenance:repair --include-expensive +run_occ config:system:set maintenance_window_start --type=integer --value=1 +run_occ app:update --all +run_occ maintenance:mode --off +echo "Post-upgrade commands completed successfully" diff --git a/app-hooks/pre-installation/.gitkeep b/app-hooks/pre-installation/.gitkeep new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/app-hooks/pre-installation/.gitkeep @@ -0,0 +1 @@ + diff --git a/app-hooks/pre-upgrade/01-check-disk-and-dump-db.sh b/app-hooks/pre-upgrade/01-check-disk-and-dump-db.sh new file mode 100755 index 0000000..99281ff --- /dev/null +++ b/app-hooks/pre-upgrade/01-check-disk-and-dump-db.sh @@ -0,0 +1,58 @@ +#!/bin/bash + +set -euo pipefail + +nextcloud_dir=${NEXTCLOUD_DIR:-/var/www/html} +backup_dir=${NEXTCLOUD_BACKUP_DIR:-/backups} +min_free_mb=${NEXTCLOUD_UPGRADE_MIN_FREE_MB:-2048} +db_host=${POSTGRES_HOST:-postgres} +db_name=${POSTGRES_DB:-nextcloud} +db_user=${POSTGRES_USER:-nextcloud} +db_password=${POSTGRES_PASSWORD:-} +timestamp=$(date +%Y%m%d-%H%M%S) +backup_file="${backup_dir}/nextcloud-db-${timestamp}.sql.gz" +app_list_file="${backup_dir}/app_list.old" + +run_occ() { + echo "Running: php occ $*" + php occ "$@" +} + +check_free_space() { + local path=$1 + local label=$2 + local available_mb + + available_mb=$(df -Pm "$path" | awk 'NR==2 { print $4 }') + + if [ "$available_mb" -lt "$min_free_mb" ]; then + echo "Not enough disk space on ${label}: ${available_mb} MB available, ${min_free_mb} MB required" + exit 1 + fi + + echo "${label} has ${available_mb} MB free" +} + +echo "Running pre-upgrade safety checks" +mkdir -p "$backup_dir" + +run_occ maintenance:mode --on +php occ app:list > "$app_list_file" +echo "Saved active apps list to ${app_list_file}" + +check_free_space "$nextcloud_dir" "Nextcloud volume" +check_free_space "$backup_dir" "Backup volume" + +if ! command -v pg_dump >/dev/null 2>&1; then + echo "pg_dump is not available in the container image" + exit 1 +fi + +if [ -z "$db_password" ]; then + echo "POSTGRES_PASSWORD is empty, refusing to create a database dump" + exit 1 +fi + +echo "Creating PostgreSQL dump at ${backup_file}" +PGPASSWORD="$db_password" pg_dump -h "$db_host" -U "$db_user" "$db_name" | gzip -9 > "$backup_file" +echo "Database dump completed successfully" diff --git a/docker-compose-garages3.yml b/docker-compose-garages3.yml index 3c8e0f2..6b73c9b 100644 --- a/docker-compose-garages3.yml +++ b/docker-compose-garages3.yml @@ -18,6 +18,12 @@ services: networks: - internal + redis: + image: redis:7-alpine + restart: unless-stopped + networks: + - internal + garage: image: dxflrs/garage:v1.0.0 restart: unless-stopped @@ -35,7 +41,12 @@ services: restart: unless-stopped volumes: - ./volumes/nextcloud:/var/www/html + - ./backups:/backups + - ./app-hooks/pre-installation:/docker-entrypoint-hooks.d/pre-installation - ./app-hooks/post-installation:/docker-entrypoint-hooks.d/post-installation + - ./app-hooks/pre-upgrade:/docker-entrypoint-hooks.d/pre-upgrade + - ./app-hooks/post-upgrade:/docker-entrypoint-hooks.d/post-upgrade + - ./app-hooks/before-starting:/docker-entrypoint-hooks.d/before-starting - ./.docker/garages3.config.php:/var/www/html/config/garages3.config.php:ro environment: - POSTGRES_DB=${POSTGRES_DB:-nextcloud} @@ -69,6 +80,7 @@ services: - host.docker.internal:host-gateway depends_on: - db + - redis - garage networks: - internal @@ -115,6 +127,7 @@ services: - host.docker.internal:host-gateway depends_on: - db + - redis - garage networks: - internal diff --git a/docker-compose.yml b/docker-compose.yml index 89b55e7..93cfca4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,6 +6,12 @@ networks: driver: bridge services: + redis: + image: redis:7-alpine + restart: unless-stopped + networks: + - internal + app: build: context: .docker/app @@ -13,7 +19,12 @@ services: NEXTCLOUD_VERSION: ${NEXTCLOUD_VERSION:-stable-fpm} volumes: - ./volumes/nextcloud:/var/www/html + - ./backups:/backups + - ./app-hooks/pre-installation:/docker-entrypoint-hooks.d/pre-installation - ./app-hooks/post-installation:/docker-entrypoint-hooks.d/post-installation + - ./app-hooks/pre-upgrade:/docker-entrypoint-hooks.d/pre-upgrade + - ./app-hooks/post-upgrade:/docker-entrypoint-hooks.d/post-upgrade + - ./app-hooks/before-starting:/docker-entrypoint-hooks.d/before-starting restart: unless-stopped environment: - POSTGRES_DB=${POSTGRES_DB:-nextcloud} @@ -33,6 +44,8 @@ services: - MAIL_FROM_ADDRESS - MAIL_DOMAIN - TZ + depends_on: + - redis networks: - internal @@ -63,6 +76,8 @@ services: - TZ volumes: - ./volumes/nextcloud:/var/www/html + depends_on: + - redis networks: - internal entrypoint: /cron.sh diff --git a/docs/README_ptBR.md b/docs/README_ptBR.md index 4fee22e..cc22a46 100644 --- a/docs/README_ptBR.md +++ b/docs/README_ptBR.md @@ -6,6 +6,7 @@ - [Após a configuração](#após-a-configuração) - [Configuração personalizada](#configuração-personalizada) - [Personalize o conteúdo do docker-compose](#personalize-o-conteúdo-do-docker-compose) + - [Hooks de upgrade do Nextcloud](#hooks-de-upgrade-do-nextcloud) - [PHP](#php) - [Execute o Nextcloud](#execute-o-nextcloud) - [Use uma versão específica do Nextcloud](#use-uma-versão-específica-do-nextcloud) @@ -78,6 +79,46 @@ docker compose exec -u www-data app ./occ db:convert-filecache-bigint Você pode fazer isso usando variáveis de ambiente e criando um arquivo chamado `docker-compose.override.yml` para adicionar novos serviços. +### Redis + +Os arquivos principais de compose agora incluem o serviço `redis` por padrão. Isso deixa o stack autocontido para instalações do Nextcloud que já usam Redis no `config.php` e evita depender de uma rede externa específica do host. + +### Hooks de upgrade do Nextcloud + +Este repositório monta os diretórios oficiais de hooks do Nextcloud Docker para permitir extensões do fluxo de instalação e upgrade sem alterar o entrypoint da imagem. + +O serviço `app` usa estes mounts: + +```yaml +services: + app: + volumes: + - ./volumes/nextcloud:/var/www/html + - ./backups:/backups + - ./app-hooks/pre-installation:/docker-entrypoint-hooks.d/pre-installation + - ./app-hooks/post-installation:/docker-entrypoint-hooks.d/post-installation + - ./app-hooks/pre-upgrade:/docker-entrypoint-hooks.d/pre-upgrade + - ./app-hooks/post-upgrade:/docker-entrypoint-hooks.d/post-upgrade + - ./app-hooks/before-starting:/docker-entrypoint-hooks.d/before-starting +``` + +Os hooks de upgrade funcionam assim: + +- `pre-upgrade`: ativa o modo de manutenção +- `pre-upgrade`: salva a lista de apps ativos em `/backups/app_list.old` +- `pre-upgrade`: verifica o espaço livre no volume do Nextcloud e no volume de backup +- `pre-upgrade`: cria um dump compactado do PostgreSQL em `/backups` +- `post-upgrade`: salva a nova lista de apps em `/backups/app_list.new` e mostra o diff quando possível +- `post-upgrade`: executa os comandos `occ` extras necessários após um upgrade maior +- `post-upgrade`: desativa o modo de manutenção ao final + +As variáveis abaixo controlam a checagem e o diretório de backup: + +- `NEXTCLOUD_BACKUP_DIR`, com padrão `/backups` +- `NEXTCLOUD_UPGRADE_MIN_FREE_MB`, com padrão `2048` + +O diretório `./backups` no host precisa ser gravável pelo `www-data` dentro do container. O recomendado é usar `www-data:www-data` com permissão `0755`. + ### Storage primário Garage S3 Use `docker-compose-garages3.yml` quando quiser que o Nextcloud grave os arquivos em um bucket Garage S3 em vez do diretório local `data/`.