From f49afc289f01bca65307627d302b17c590f5a2de Mon Sep 17 00:00:00 2001 From: Nicolas MASSE Date: Wed, 17 Dec 2025 18:42:23 +0100 Subject: [PATCH] work in progress --- Makefile.common | 95 +++++++++++++++++++++----- README.md | 1 + generate-butane-spec.sh | 8 ++- nextcloud/Makefile | 64 ++++++++++++++--- nextcloud/config/config.env | 3 + nextcloud/config/custom-noinit.sh | 4 ++ nextcloud/config/custom-post.sh | 14 ++++ nextcloud/config/custom-pre.sh | 7 ++ nextcloud/config/redis-session.ini | 0 nextcloud/config/redis.conf | 9 ++- nextcloud/nextcloud-app.container | 22 +++--- nextcloud/nextcloud-cron.container | 51 ++++++++++++++ nextcloud/nextcloud-cron.timer | 12 ++++ nextcloud/nextcloud-init.container | 55 +++++++++++++++ nextcloud/nextcloud-nginx.container | 2 +- nextcloud/nextcloud-redis.container | 7 +- nextcloud/nextcloud-upgrade.container | 57 ++++++++++++++++ nextcloud/nextcloud.target | 5 +- nextcloud/sysctl.d/nextcloud.conf | 3 + nextcloud/tmpfiles.d/nextcloud.conf | 2 + postgresql/Makefile | 6 +- postgresql/postgresql-backup.container | 4 +- postgresql/postgresql-backup.timer | 11 +++ postgresql/postgresql-server.container | 7 +- postgresql/postgresql.target | 3 +- postgresql/sysctl.d/postgresql.conf | 3 + postgresql/tmpfiles.d/postgresql.conf | 1 + 27 files changed, 396 insertions(+), 60 deletions(-) create mode 100755 nextcloud/config/custom-noinit.sh create mode 100755 nextcloud/config/custom-post.sh create mode 100755 nextcloud/config/custom-pre.sh delete mode 100644 nextcloud/config/redis-session.ini create mode 100644 nextcloud/nextcloud-cron.container create mode 100644 nextcloud/nextcloud-cron.timer create mode 100644 nextcloud/nextcloud-init.container create mode 100644 nextcloud/nextcloud-upgrade.container create mode 100644 nextcloud/sysctl.d/nextcloud.conf create mode 100644 nextcloud/tmpfiles.d/nextcloud.conf create mode 100644 postgresql/postgresql-backup.timer create mode 100644 postgresql/sysctl.d/postgresql.conf create mode 100644 postgresql/tmpfiles.d/postgresql.conf diff --git a/Makefile.common b/Makefile.common index e31823d..f441ade 100644 --- a/Makefile.common +++ b/Makefile.common @@ -1,4 +1,6 @@ -.PHONY: all install install-etc install-var uninstall pre-requisites clean dryrun tail-logs butane help fcos-vm clean-vm console +.PHONY: all install install-etc install-var uninstall pre-requisites clean dryrun +.PHONY: tail-logs butane help fcos-vm clean-vm console +.PHONY: clean-pre clean-post install-pre install-post uninstall-pre uninstall-post all: help help: @@ -13,7 +15,6 @@ help: @echo " fcos-vm - Launch a Fedora CoreOS VM with the generated Butane spec" @echo " clean-vm - Clean up the Fedora CoreOS VM and its resources" @echo " console - Connect to the Fedora CoreOS VM console" - TARGET_CHROOT ?= PROJECT_NAME := $(shell basename "$${PWD}") @@ -28,10 +29,17 @@ QUADLET_UNIT_NAMES := $(patsubst %.container, %.service, $(wildcard *.container) $(patsubst %.pod, %-pod.service, $(wildcard *.pod)) \ $(patsubst %.build, %-build.service, $(wildcard *.build)) CONFIG_FILES = $(wildcard config/*) +TMPFILESD_FILES = $(wildcard tmpfiles.d/*) +SYSCTLD_FILES = $(wildcard sysctl.d/*) TARGET_QUADLETS_FILES = $(addprefix $(TARGET_CHROOT)/etc/containers/systemd/, $(QUADLETS_FILES)) TARGET_SYSTEMD_FILES = $(addprefix $(TARGET_CHROOT)/etc/systemd/system/, $(SYSTEMD_FILES)) TARGET_CONFIG_FILES = $(patsubst config/%, $(TARGET_CHROOT)/etc/quadlets/$(PROJECT_NAME)/%, $(CONFIG_FILES)) TARGET_FILES = $(TARGET_QUADLETS_FILES) $(TARGET_SYSTEMD_FILES) $(TARGET_CONFIG_FILES) +TARGET_TMPFILESD_FILES = $(patsubst tmpfiles.d/%, $(TARGET_CHROOT)/etc/tmpfiles.d/%, $(TMPFILESD_FILES)) +TARGET_SYSCTLD_FILES = $(patsubst sysctl.d/%, $(TARGET_CHROOT)/etc/sysctl.d/%, $(SYSCTLD_FILES)) +DEPENDENCIES ?= +I_KNOW_WHAT_I_AM_DOING ?= +DEPENDENCIES_IGNITION_FILES = $(shell for dep in $(DEPENDENCIES); do echo $(TOP_LEVEL_DIR)/$$dep/$$dep.ign; done) pre-requisites: @if [ -z "$(TOP_LEVEL_DIR)" ]; then \ @@ -55,6 +63,12 @@ $(TARGET_CHROOT)/etc/systemd/system: $(TARGET_CHROOT)/etc/quadlets/$(PROJECT_NAME): install -D -d -m 0755 -o root -g root $@ +$(TARGET_CHROOT)/etc/tmpfiles.d: + install -D -d -m 0755 -o root -g root $@ + +$(TARGET_CHROOT)/etc/sysctl.d: + install -D -d -m 0755 -o root -g root $@ + $(TARGET_CHROOT)/etc/containers/systemd/%.container: %.container $(TARGET_CHROOT)/etc/containers/systemd install -m 0644 -o root -g root $< $@ @@ -90,25 +104,58 @@ $(TARGET_CHROOT)/etc/quadlets/$(PROJECT_NAME)/%: config/% $(TARGET_CHROOT)/etc/q $(TARGET_CHROOT)/var/lib/quadlets/$(PROJECT_NAME): install -d -m 0755 -o root -g root $@ -install-etc: $(TARGET_QUADLETS_FILES) $(TARGET_SYSTEMD_FILES) $(TARGET_CONFIG_FILES) +$(TARGET_CHROOT)/etc/tmpfiles.d/%: tmpfiles.d/% $(TARGET_CHROOT)/etc/tmpfiles.d + install -D -m 0644 -o root -g root $< $@ + +$(TARGET_CHROOT)/etc/sysctl.d/%: sysctl.d/% $(TARGET_CHROOT)/etc/sysctl.d + install -D -m 0644 -o root -g root $< $@ + +install-etc: $(TARGET_QUADLETS_FILES) $(TARGET_SYSTEMD_FILES) $(TARGET_CONFIG_FILES) $(TARGET_TMPFILESD_FILES) $(TARGET_SYSCTLD_FILES) install-var: $(TARGET_CHROOT)/var/lib/quadlets/$(PROJECT_NAME) -install: pre-requisites dryrun install-etc install-var +install-pre:: + @run() { echo $$*; "$$@"; }; \ + for dep in $(DEPENDENCIES); do \ + run $(MAKE) -C $(TOP_LEVEL_DIR)/$$dep install; \ + done + +install-post:: +install: pre-requisites dryrun install-etc install-var install-pre systemctl daemon-reload systemd-analyze --generators=true verify $(QUADLET_UNIT_NAMES) $(SYSTEMD_UNIT_NAMES) - systemctl enable $(SYSTEMD_UNIT_NAMES) + @run() { echo $$*; "$$@"; }; \ + if [ -f /etc/tmpfiles.d/$(PROJECT_NAME).conf ]; then \ + run systemd-tmpfiles --create /etc/tmpfiles.d/$(PROJECT_NAME).conf; \ + fi; \ + if [ -f /etc/sysctl.d/$(PROJECT_NAME).conf ]; then \ + run sysctl -q -p /etc/sysctl.d/$(PROJECT_NAME).conf; \ + fi + systemctl enable $(SYSTEMD_MAIN_UNIT_NAMES) $(SYSTEMD_TIMER_NAMES) systemctl start $(SYSTEMD_MAIN_UNIT_NAMES) + $(MAKE) install-post -uninstall: pre-requisites - systemctl --no-block disable $(SYSTEMD_UNIT_NAMES) || true - systemctl --no-block stop $(SYSTEMD_UNIT_NAMES) $(QUADLET_UNIT_NAMES) || true +uninstall-pre:: +uninstall-post:: + @run() { echo $$*; "$$@"; }; \ + for dep in $(DEPENDENCIES); do \ + run $(MAKE) -C $(TOP_LEVEL_DIR)/$$dep uninstall; \ + done + +uninstall: pre-requisites uninstall-pre + systemctl disable $(SYSTEMD_MAIN_UNIT_NAMES) $(SYSTEMD_TIMER_NAMES) || true + systemctl stop $(SYSTEMD_UNIT_NAMES) $(QUADLET_UNIT_NAMES) || true + @run() { echo $$*; "$$@"; }; \ + if [ -f /etc/tmpfiles.d/$(PROJECT_NAME).conf ]; then \ + run systemd-tmpfiles --purge /etc/tmpfiles.d/$(PROJECT_NAME).conf; \ + fi rm -f $(TARGET_QUADLETS_FILES) $(TARGET_SYSTEMD_FILES) $(TARGET_CONFIG_FILES) systemctl daemon-reload + $(MAKE) uninstall-post tail-logs: pre-requisites @run() { echo $$*; "$$@"; }; \ declare -a journalctl_args=( -f ); \ - for unit in $(SYSTEMD_MAIN_UNIT_NAMES) $(QUADLET_UNIT_NAMES); do \ + for unit in $(SYSTEMD_UNIT_NAMES) $(QUADLET_UNIT_NAMES); do \ journalctl_args+=( -u "$$unit" ); \ done; \ run journalctl "$${journalctl_args[@]}" @@ -133,7 +180,12 @@ butane: $(TOP_LEVEL_DIR)/local.ign: $(TOP_LEVEL_DIR)/local.bu butane --strict -o $@ $< -fcos.ign: fcos.bu $(TOP_LEVEL_DIR)/local.ign $(PROJECT_NAME).ign +# Because we don't know how to build this file, it is safer to declare it as phony and let the Makefile of the dependency handle it. +.PHONY: $(DEPENDENCIES_IGNITION_FILES) +$(DEPENDENCIES_IGNITION_FILES): + $(MAKE) -C $(dir $@) $(notdir $@) + +fcos.ign: fcos.bu $(TOP_LEVEL_DIR)/local.ign $(PROJECT_NAME).ign $(DEPENDENCIES_IGNITION_FILES) @run() { echo $$*; "$$@"; }; \ tmp=$$(mktemp -d /tmp/butane-XXXXXX); \ run cp $(filter %.ign,$^) $$tmp; \ @@ -170,14 +222,23 @@ clean-vm: pre-requisites rm -rf /var/lib/libvirt/images/$(PROJECT_NAME) console: pre-requisites - @while sleep 2; do virsh console $(PROJECT_NAME); done + @while sleep 2; do virsh console $(PROJECT_NAME); echo -e "Disconnected. Reconnecting in 2 seconds...\nPress Ctrl-C to abort.\n"; done -clean: pre-requisites +clean-pre:: + @run() { echo $$*; "$$@"; }; \ + for dep in $(DEPENDENCIES); do \ + run $(MAKE) -C $(TOP_LEVEL_DIR)/$$dep clean; \ + done + +clean-post:: +clean: clean-pre pre-requisites rm -f *.butane @run() { echo $$*; "$$@"; }; \ - read -p "This will remove all data of '$(PROJECT_NAME)'. Are you sure? (only 'yes' is accepted) " ans; \ - if [ "$$ans" = "yes" ] || [ "$$ans" = "YES" ]; then \ - run rm -rf /var/lib/quadlets/$(PROJECT_NAME)/ /var/run/quadlets/$(PROJECT_NAME)/ /etc/quadlets/$(PROJECT_NAME)/; \ - else \ - echo "Aborted."; exit 1; \ + if [ "$(I_KNOW_WHAT_I_AM_DOING)" != "yes" ]; then \ + read -p "This will remove all data of '$(PROJECT_NAME)'. Are you sure? (only 'yes' is accepted) " ans; \ + if [ "$$ans" != "yes" ] && [ "$$ans" != "YES" ]; then \ + echo "Aborted."; exit 1; \ + fi; \ fi + rm -rf /var/lib/quadlets/$(PROJECT_NAME)/ /var/run/quadlets/$(PROJECT_NAME)/ /etc/quadlets/$(PROJECT_NAME)/ + $(MAKE) clean-post diff --git a/README.md b/README.md index 860f757..7bcf573 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ This repository gathers all the recipes (hence the name "Cookbook") to deploy Op - [nginx](nginx/): starts Nginx, content is initialized / updated from a GIT repository - [postgresql](postgresql/): starts a PostgreSQL server, handles automated major upgrades, periodic backup and initialization of the database from the last backup. +- [nextcloud](nextcloud/): starts a Nextcloud server with all its dependencies, handles automated upgrades. ## License diff --git a/generate-butane-spec.sh b/generate-butane-spec.sh index 0b77027..e3b5646 100755 --- a/generate-butane-spec.sh +++ b/generate-butane-spec.sh @@ -35,9 +35,9 @@ for file in $(find "$TARGET_CHROOT" \! -type d); do group: id: $(stat -c '%g' "$file") contents: - compression: gzip - source: data:;base64,$(gzip -c "$file" | base64 -w0) + inline: | EOF + sed 's/^/ /; $s/$/\n/' "$file" done cat <<"EOF" directories: @@ -45,8 +45,10 @@ EOF for dir in $(find "$TARGET_CHROOT" -type d); do rel_path="${dir#$TARGET_CHROOT}" if [[ "$rel_path" != "/var/lib/quadlets/"* ]] && [[ "$rel_path" != "/etc/quadlets/"* ]] \ - && [[ "$rel_path" != "/etc/systemd/system/"* ]] && [[ "$rel_path" != "/etc/containers/systemd/"* ]]; then + && [[ "$rel_path" != "/etc/systemd/system/"* ]] && [[ "$rel_path" != "/etc/containers/systemd/"* ]] \ + && [[ "$rel_path" != "/etc/tmpfiles.d/"* ]] && [[ "$rel_path" != "/etc/sysctl.d/"* ]]; then + # Skip files & directories that are already part of the CoreOS default installation continue fi cat < /dev/null; do \ + echo "Nextcloud is not ready yet. Retrying in 5 seconds..."; \ + sleep 5; \ + done; \ + echo "Nextcloud is ready!"; \ echo "Uploading file..."; \ run curl -X PUT -sSf -u "$${NEXTCLOUD_ADMIN_USER}:$${NEXTCLOUD_ADMIN_PASSWORD}" --data-binary @tests/witness.txt "$${OVERWRITECLIURL}/remote.php/webdav/witness.txt"; \ echo "Verifying file upload..."; \ @@ -31,5 +41,37 @@ test: uninstall clean install else \ echo "File upload verification failed!"; \ exit 1; \ - fi - + fi; \ + sleep 2; \ + for (( ver=$(NEXTCLOUD_MAJOR_START); ver<$(NEXTCLOUD_MAJOR_LAST); ver++ )); do \ + nextver=$$(($$ver + 1)); \ + echo "Upgrading Nextcloud from $$ver to $$nextver..."; \ + sed -i "s/^NEXTCLOUD_MAJOR=.*/NEXTCLOUD_MAJOR=$$nextver/" /etc/quadlets/nextcloud/config.env; \ + systemctl stop nextcloud.target; \ + sleep 1; \ + systemctl start nextcloud.target; \ + echo "Waiting for Nextcloud to be ready..."; \ + until run curl -X GET -sSf -u "$${NEXTCLOUD_ADMIN_USER}:$${NEXTCLOUD_ADMIN_PASSWORD}" "$${OVERWRITECLIURL}/status.php" &> /dev/null; do \ + echo "Nextcloud is not ready yet. Retrying in 5 seconds..."; \ + sleep 5; \ + done; \ + echo "Nextcloud is ready after upgrade!"; \ + echo "Verifying file upload after upgrade..."; \ + run curl -X GET -sSf -u "$${NEXTCLOUD_ADMIN_USER}:$${NEXTCLOUD_ADMIN_PASSWORD}" "$${OVERWRITECLIURL}/remote.php/webdav/witness.txt" -o /tmp/witness.txt; \ + if run cmp -s tests/witness.txt /tmp/witness.txt ; then \ + echo "File upload verified successfully after upgrade!"; \ + else \ + echo "File upload verification failed after upgrade!"; \ + exit 1; \ + fi; \ + # Assert Nextcloud version after upgrade from status.php \ + ACTUAL_VERSION=$$(run curl -X GET -sSf -u "$${NEXTCLOUD_ADMIN_USER}:$${NEXTCLOUD_ADMIN_PASSWORD}" "$${OVERWRITECLIURL}/status.php" | grep -oP '"version":"\K[^"]+'); \ + EXPECTED_VERSION_PREFIX="$$nextver."; \ + if [[ "$$ACTUAL_VERSION" == "$$EXPECTED_VERSION_PREFIX"* ]]; then \ + echo "Nextcloud version $$ACTUAL_VERSION verified successfully after upgrade to $$nextver!"; \ + else \ + echo "Nextcloud version verification failed after upgrade to $$nextver! Expected prefix: $$EXPECTED_VERSION_PREFIX, Actual: $$ACTUAL_VERSION"; \ + exit 1; \ + fi; \ + done; \ + echo "Nextcloud upgrade tests completed successfully." diff --git a/nextcloud/config/config.env b/nextcloud/config/config.env index 0ccbdd8..c6770d5 100644 --- a/nextcloud/config/config.env +++ b/nextcloud/config/config.env @@ -2,6 +2,9 @@ ## Nextcloud Configuration Environment Variables ## +# Nextcloud version +NEXTCLOUD_MAJOR=31 + # Nextcloud domain configuration NEXTCLOUD_TRUSTED_DOMAINS=localhost OVERWRITEHOST=localhost diff --git a/nextcloud/config/custom-noinit.sh b/nextcloud/config/custom-noinit.sh new file mode 100755 index 0000000..62c85ee --- /dev/null +++ b/nextcloud/config/custom-noinit.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +echo "This container is not intented to perform initialization or upgrade tasks." +exit 1 diff --git a/nextcloud/config/custom-post.sh b/nextcloud/config/custom-post.sh new file mode 100755 index 0000000..3e0bd15 --- /dev/null +++ b/nextcloud/config/custom-post.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +set -Eeuo pipefail + +# Disable maintenance mode +echo "Disabling maintenance mode..." +php occ maintenance:mode --off + +# Run database optimizations +echo "Adding missing database indices..." +php occ db:add-missing-indices || true + +echo "Converting database columns to big int..." +php occ db:convert-filecache-bigint --no-interaction || true diff --git a/nextcloud/config/custom-pre.sh b/nextcloud/config/custom-pre.sh new file mode 100755 index 0000000..ddb41cd --- /dev/null +++ b/nextcloud/config/custom-pre.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +set -Eeuo pipefail + +# Enable maintenance mode +echo "Enabling maintenance mode..." +php occ maintenance:mode --on || true diff --git a/nextcloud/config/redis-session.ini b/nextcloud/config/redis-session.ini deleted file mode 100644 index e69de29..0000000 diff --git a/nextcloud/config/redis.conf b/nextcloud/config/redis.conf index b64d2b2..11cceb4 100644 --- a/nextcloud/config/redis.conf +++ b/nextcloud/config/redis.conf @@ -1,3 +1,10 @@ -requirepass nextcloud +# Network settings port 6379 bind 127.0.0.1 + +# Set a password for Redis +requirepass nextcloud + +# Hybrid mode (AOF + RDB) +appendonly yes +aof-use-rdb-preamble yes diff --git a/nextcloud/nextcloud-app.container b/nextcloud/nextcloud-app.container index 0af8926..4edc3b7 100644 --- a/nextcloud/nextcloud-app.container +++ b/nextcloud/nextcloud-app.container @@ -1,22 +1,20 @@ [Unit] Description=Nextcloud PHP-FPM Application Documentation=https://hub.docker.com/_/nextcloud/ -After=network.target nextcloud-redis.service postgresql-server.service -Requires=nextcloud-redis.service postgresql-server.service - -# Require initialization to complete first -Requires=nextcloud-redis.service -After=nextcloud-redis.service +After=network.target nextcloud-redis.service postgresql-server.service nextcloud-upgrade.service +Requires=nextcloud-redis.service postgresql-server.service nextcloud-upgrade.service # Only start if Nextcloud has been configured ConditionPathExists=/etc/quadlets/nextcloud/config.env +# and initialized (config.php exists) +ConditionPathExists=/var/lib/quadlets/nextcloud/data/config/config.php # Start/stop this unit when the target is started/stopped PartOf=nextcloud.target [Container] ContainerName=nextcloud-app -Image=docker.io/library/nextcloud:31-fpm-alpine +Image=docker.io/library/nextcloud:${NEXTCLOUD_MAJOR}-fpm-alpine # Fix the UID/GID of the PHP-FPM daemon User=82:82 @@ -30,8 +28,9 @@ EnvironmentFile=/etc/quadlets/nextcloud/config.env # Volume mounts Volume=/var/lib/quadlets/nextcloud/data:/var/www/html:z -Volume=/var/lib/quadlets/nextcloud/config/www.conf:/usr/local/etc/php-fpm.d/www.conf:Z -Volume=/var/lib/quadlets/nextcloud/config/redis-session.ini:/usr/local/etc/php/conf.d/redis-session.ini:Z +Volume=/etc/quadlets/nextcloud/www.conf:/usr/local/etc/php-fpm.d/www.conf:Z +Volume=/run/quadlets/nextcloud/redis-session.ini:/usr/local/etc/php/conf.d/redis-session.ini:Z +Volume=/etc/quadlets/nextcloud/custom-noinit.sh:/docker-entrypoint-hooks.d/pre-installation/custom.sh:z,ro # Health check (equivalent to readiness probe) HealthCmd=nc -z localhost 9000 @@ -46,9 +45,8 @@ RestartSec=10 TimeoutStartSec=600 TimeoutStopSec=30 -# Skaffold filesystem + fix permissions -ExecStartPre=/bin/bash -Eeuo pipefail -c 'install -m 0700 -o 82 -g 82 /etc/quadlets/nextcloud/www.conf /var/lib/quadlets/nextcloud/config/www.conf ; \ - install -m 0700 -o 82 -g 82 /etc/quadlets/nextcloud/redis-session.ini /var/lib/quadlets/nextcloud/config/redis-session.ini' +# These environment variables are sourced to be used by systemd in the Exec* commands +EnvironmentFile=/etc/quadlets/nextcloud/config.env # Wait for PostgreSQL to be ready on localhost ExecStartPre=/bin/sh -c 'exec 2>/dev/null; for try in $(seq 0 12); do if ! /bin/true 5<> /dev/tcp/127.0.0.1/5432; then echo "Waiting for PostgreSQL to be available..."; sleep 5; else exit 0; fi; done; exit 1' diff --git a/nextcloud/nextcloud-cron.container b/nextcloud/nextcloud-cron.container new file mode 100644 index 0000000..f40fdb6 --- /dev/null +++ b/nextcloud/nextcloud-cron.container @@ -0,0 +1,51 @@ +[Unit] +Description=Nextcloud Application - Cron Job +Documentation=https://hub.docker.com/_/nextcloud/ +After=nextcloud-redis.service postgresql-server.service +Requires=nextcloud-redis.service postgresql-server.service + +# Only start if Nextcloud has been configured +ConditionPathExists=/etc/quadlets/nextcloud/config.env +# and initialized (config.php exists) +ConditionPathExists=/var/lib/quadlets/nextcloud/data/config/config.php + +[Container] +ContainerName=nextcloud-cron-job +Image=docker.io/library/nextcloud:${NEXTCLOUD_MAJOR}-fpm-alpine + +# Fix the UID/GID of the PHP-FPM daemon +User=82:82 + +# Network configuration +Network=host + +# Environment variables from config +EnvironmentFile=/etc/quadlets/nextcloud/config.env + +# This is specific to the cron container +Entrypoint=php +Exec=-f /var/www/html/cron.php + +# Volume mounts +Volume=/var/lib/quadlets/nextcloud/data:/var/www/html:z +Volume=/etc/quadlets/nextcloud/www.conf:/usr/local/etc/php-fpm.d/www.conf:Z +Volume=/run/quadlets/nextcloud/redis-session.ini:/usr/local/etc/php/conf.d/redis-session.ini:Z + +[Service] +Restart=no +TimeoutStartSec=600 + +# These environment variables are sourced to be used by systemd in the Exec* commands +EnvironmentFile=/etc/quadlets/nextcloud/config.env + +# This container is a job - run once to completion +Type=oneshot + +# Wait for PostgreSQL to be ready +ExecStartPre=/bin/sh -c 'exec 2>/dev/null; for try in $(seq 0 12); do if ! /bin/true 5<> /dev/tcp/127.0.0.1/5432; then echo "Waiting for PostgreSQL to be available..."; sleep 5; else exit 0; fi; done; exit 1' + +# Wait for Redis to be ready +ExecStartPre=/bin/sh -c 'exec 2>/dev/null; for try in $(seq 0 12); do if ! /bin/true 5<> /dev/tcp/127.0.0.1/6379; then echo "Waiting for Redis to be available..."; sleep 5; else exit 0; fi; done; exit 1' + +[Install] +WantedBy=nextcloud-cron.timer diff --git a/nextcloud/nextcloud-cron.timer b/nextcloud/nextcloud-cron.timer new file mode 100644 index 0000000..19ea3fa --- /dev/null +++ b/nextcloud/nextcloud-cron.timer @@ -0,0 +1,12 @@ +[Unit] +Description=Nextcloud Application - Initialization +Documentation=https://hub.docker.com/_/nextcloud/ +PartOf=nextcloud.target + +[Timer] +OnActiveSec=15min +RandomizedDelaySec=15s +DeferReactivation=true + +[Install] +WantedBy=nextcloud.target diff --git a/nextcloud/nextcloud-init.container b/nextcloud/nextcloud-init.container new file mode 100644 index 0000000..013ddec --- /dev/null +++ b/nextcloud/nextcloud-init.container @@ -0,0 +1,55 @@ +[Unit] +Description=Nextcloud Application - Initialization +Documentation=https://hub.docker.com/_/nextcloud/ +After=network.target nextcloud-redis.service postgresql-server.service +Before=nextcloud-app.service +Requires=nextcloud-redis.service postgresql-server.service + +# Only start if Nextcloud has been configured +ConditionPathExists=/etc/quadlets/nextcloud/config.env +# and NOT initialized (config.php does NOT exist) +ConditionPathExists=!/var/lib/quadlets/nextcloud/data/config/config.php + +# Start/stop this unit when the target is started/stopped +#PartOf=nextcloud.target + +[Container] +ContainerName=nextcloud-init-job +Image=docker.io/library/nextcloud:${NEXTCLOUD_MAJOR}-fpm-alpine + +# Fix the UID/GID of the PHP-FPM daemon +User=82:82 + +# Network configuration +Network=host + +# Environment variables from config +EnvironmentFile=/etc/quadlets/nextcloud/config.env + +# This is specific to the initialization container +Environment=NEXTCLOUD_UPDATE=1 +Exec=/bin/true + +# Volume mounts +Volume=/var/lib/quadlets/nextcloud/data:/var/www/html:z +Volume=/etc/quadlets/nextcloud/www.conf:/usr/local/etc/php-fpm.d/www.conf:Z +Volume=/run/quadlets/nextcloud/redis-session.ini:/usr/local/etc/php/conf.d/redis-session.ini:Z + +[Service] +Restart=no +TimeoutStartSec=600 + +# These environment variables are sourced to be used by systemd in the Exec* commands +EnvironmentFile=/etc/quadlets/nextcloud/config.env + +# This container is a job - run once to completion +Type=oneshot + +# Wait for PostgreSQL to be ready +ExecStartPre=/bin/sh -c 'exec 2>/dev/null; for try in $(seq 0 12); do if ! /bin/true 5<> /dev/tcp/127.0.0.1/5432; then echo "Waiting for PostgreSQL to be available..."; sleep 5; else exit 0; fi; done; exit 1' + +# Wait for Redis to be ready +ExecStartPre=/bin/sh -c 'exec 2>/dev/null; for try in $(seq 0 12); do if ! /bin/true 5<> /dev/tcp/127.0.0.1/6379; then echo "Waiting for Redis to be available..."; sleep 5; else exit 0; fi; done; exit 1' + +[Install] +WantedBy=nextcloud.target diff --git a/nextcloud/nextcloud-nginx.container b/nextcloud/nextcloud-nginx.container index 1153634..f45c4c2 100644 --- a/nextcloud/nextcloud-nginx.container +++ b/nextcloud/nextcloud-nginx.container @@ -29,7 +29,7 @@ Volume=/etc/quadlets/nextcloud/nginx.conf:/etc/nginx/nginx.conf:ro HealthCmd=curl -sSfL http://localhost/status.php HealthInterval=30s HealthTimeout=10s -HealthStartPeriod=30s +HealthStartPeriod=10s HealthRetries=3 [Service] diff --git a/nextcloud/nextcloud-redis.container b/nextcloud/nextcloud-redis.container index 6cf05d8..ce40344 100644 --- a/nextcloud/nextcloud-redis.container +++ b/nextcloud/nextcloud-redis.container @@ -1,6 +1,6 @@ [Unit] Description=Redis Cache for Nextcloud -Documentation=https://redis.io/ +Documentation=https://hub.docker.com/_/redis/ After=network.target # Only start if Nextcloud has been configured @@ -27,7 +27,7 @@ Volume=/var/lib/quadlets/nextcloud/redis:/data:Z Volume=/etc/quadlets/nextcloud/redis.conf:/usr/local/etc/redis/redis.conf:ro # Health check -HealthCmd=redis-cli ping -t 5 | grep -q PONG +HealthCmd=redis-cli -t 5 ping | grep -qFx PONG HealthInterval=30s HealthTimeout=5s HealthStartPeriod=10s @@ -39,5 +39,8 @@ RestartSec=5 TimeoutStartSec=300 TimeoutStopSec=30 +# These environment variables are sourced to be used by systemd in the Exec* commands +EnvironmentFile=/etc/quadlets/nextcloud/config.env + [Install] WantedBy=nextcloud.target \ No newline at end of file diff --git a/nextcloud/nextcloud-upgrade.container b/nextcloud/nextcloud-upgrade.container new file mode 100644 index 0000000..deb2fe6 --- /dev/null +++ b/nextcloud/nextcloud-upgrade.container @@ -0,0 +1,57 @@ +[Unit] +Description=Nextcloud Application - Upgrade +Documentation=https://hub.docker.com/_/nextcloud/ +After=network.target nextcloud-redis.service postgresql-server.service +Before=nextcloud-app.service +Requires=nextcloud-redis.service postgresql-server.service + +# Only start if Nextcloud has been configured +ConditionPathExists=/etc/quadlets/nextcloud/config.env +# and initialized (config.php exists) +ConditionPathExists=/var/lib/quadlets/nextcloud/data/config/config.php + +# Start/stop this unit when the target is started/stopped +PartOf=nextcloud.target + +[Container] +ContainerName=nextcloud-upgrade-to-${NEXTCLOUD_MAJOR}-job +Image=docker.io/library/nextcloud:${NEXTCLOUD_MAJOR}-fpm-alpine + +# Fix the UID/GID of the PHP-FPM daemon +User=82:82 + +# Network configuration +Network=host + +# Environment variables from config +EnvironmentFile=/etc/quadlets/nextcloud/config.env + +# This is specific to the upgrade container +Environment=NEXTCLOUD_UPDATE=1 +Exec=/bin/true +Volume=/etc/quadlets/nextcloud/custom-pre.sh:/docker-entrypoint-hooks.d/pre-upgrade/custom.sh:z,ro +Volume=/etc/quadlets/nextcloud/custom-post.sh:/docker-entrypoint-hooks.d/post-upgrade/custom.sh:z,ro + +# Volume mounts +Volume=/var/lib/quadlets/nextcloud/data:/var/www/html:z +Volume=/etc/quadlets/nextcloud/www.conf:/usr/local/etc/php-fpm.d/www.conf:Z +Volume=/run/quadlets/nextcloud/redis-session.ini:/usr/local/etc/php/conf.d/redis-session.ini:Z + +[Service] +Restart=no +TimeoutStartSec=600 + +# These environment variables are sourced to be used by systemd in the Exec* commands +EnvironmentFile=/etc/quadlets/nextcloud/config.env + +# This container is a job - run once to completion +Type=oneshot + +# Wait for PostgreSQL to be ready +ExecStartPre=/bin/sh -c 'exec 2>/dev/null; for try in $(seq 0 12); do if ! /bin/true 5<> /dev/tcp/127.0.0.1/5432; then echo "Waiting for PostgreSQL to be available..."; sleep 5; else exit 0; fi; done; exit 1' + +# Wait for Redis to be ready +ExecStartPre=/bin/sh -c 'exec 2>/dev/null; for try in $(seq 0 12); do if ! /bin/true 5<> /dev/tcp/127.0.0.1/6379; then echo "Waiting for Redis to be available..."; sleep 5; else exit 0; fi; done; exit 1' + +[Install] +WantedBy=nextcloud.target diff --git a/nextcloud/nextcloud.target b/nextcloud/nextcloud.target index bc11488..d1bc80b 100644 --- a/nextcloud/nextcloud.target +++ b/nextcloud/nextcloud.target @@ -1,8 +1,9 @@ [Unit] Description=Nextcloud Service Target Documentation=man:systemd.target(5) -Requires=postgresql.target nextcloud-redis.service nextcloud-app.service nextcloud-nginx.service -After=postgresql.target nextcloud-redis.service nextcloud-app.service nextcloud-nginx.service +Requires=postgresql.target nextcloud-redis.service nextcloud-nginx.service nextcloud-app.service nextcloud-upgrade.service nextcloud-cron.timer +After=postgresql.target nextcloud-redis.service nextcloud-nginx.service nextcloud-app.service nextcloud-upgrade.service +Before=nextcloud-cron.timer # Allow isolation - can stop/start this target independently AllowIsolate=yes diff --git a/nextcloud/sysctl.d/nextcloud.conf b/nextcloud/sysctl.d/nextcloud.conf new file mode 100644 index 0000000..a40a5e6 --- /dev/null +++ b/nextcloud/sysctl.d/nextcloud.conf @@ -0,0 +1,3 @@ +# Redis recommended settings for stability +# See https://redis.io/docs/latest/develop/get-started/faq/#background-saving-fails-with-a-fork-error-on-linux +#vm.overcommit_memory=1 diff --git a/nextcloud/tmpfiles.d/nextcloud.conf b/nextcloud/tmpfiles.d/nextcloud.conf new file mode 100644 index 0000000..18fff00 --- /dev/null +++ b/nextcloud/tmpfiles.d/nextcloud.conf @@ -0,0 +1,2 @@ +d$ /run/quadlets/nextcloud 0700 82 82 - +f+$ /run/quadlets/nextcloud/redis-session.ini 0600 82 82 - diff --git a/postgresql/Makefile b/postgresql/Makefile index 1cf9b81..2f6e3ee 100644 --- a/postgresql/Makefile +++ b/postgresql/Makefile @@ -1,17 +1,17 @@ TOP_LEVEL_DIR := .. include $(TOP_LEVEL_DIR)/Makefile.common -.PHONY: test test-set-pgmajor +.PHONY: test test-set-pgmajor install-var PG_MAJOR_START ?= 14 PG_MAJOR_LAST ?= 18 test-set-pgmajor: sed -i 's/^PG_MAJOR=.*/PG_MAJOR=$(PG_MAJOR_START)/' config/config.env -$(TARGET_CHROOT)/var/lib/quadlets/postgresql/backup $(TARGET_CHROOT)/var/lib/quadlets/postgresql $(TARGET_CHROOT)/var/run/quadlets/postgresql: +$(TARGET_CHROOT)/var/lib/quadlets/postgresql/backup $(TARGET_CHROOT)/var/lib/quadlets/postgresql: install -m 0700 -o 70 -g 70 -d $@ -install-var: $(TARGET_CHROOT)/var/run/quadlets/postgresql $(TARGET_CHROOT)/var/lib/quadlets/postgresql $(TARGET_CHROOT)/var/lib/quadlets/postgresql/backup +install-var: $(TARGET_CHROOT)/var/lib/quadlets/postgresql/backup test: uninstall clean test-set-pgmajor install @echo "Running PostgreSQL integration tests..."; \ diff --git a/postgresql/postgresql-backup.container b/postgresql/postgresql-backup.container index a8d85e5..0d5860d 100644 --- a/postgresql/postgresql-backup.container +++ b/postgresql/postgresql-backup.container @@ -27,8 +27,8 @@ User=postgres Volume=/var/lib/quadlets/postgresql:/var/lib/postgresql:z Volume=/etc/quadlets/postgresql/backup.sh:/usr/local/bin/backup.sh:z,ro -# Share /var/run/postgresql/ between containers in the pod for the Unix socket -Volume=/var/run/quadlets/postgresql:/var/run/postgresql:z +# Share /run/postgresql/ between containers in the pod for the Unix socket +Volume=/run/quadlets/postgresql:/var/run/postgresql:z [Service] Restart=no diff --git a/postgresql/postgresql-backup.timer b/postgresql/postgresql-backup.timer new file mode 100644 index 0000000..dcd89e5 --- /dev/null +++ b/postgresql/postgresql-backup.timer @@ -0,0 +1,11 @@ +[Unit] +Description=PostgreSQL Database Backup Timer +Documentation=https://hub.docker.com/_/postgresql/ +PartOf=postgresql.target + +[Timer] +OnCalendar=daily +RandomizedDelaySec=15min + +[Install] +WantedBy=postgresql.target diff --git a/postgresql/postgresql-server.container b/postgresql/postgresql-server.container index dcdc248..e3bd68f 100644 --- a/postgresql/postgresql-server.container +++ b/postgresql/postgresql-server.container @@ -47,8 +47,8 @@ HealthTimeout=10s HealthStartPeriod=60s HealthRetries=3 -# Share /var/run/postgresql/ between containers in the pod for the Unix socket -Volume=/var/run/quadlets/postgresql:/var/run/postgresql:z +# Share /run/postgresql/ between containers in the pod for the Unix socket +Volume=/run/quadlets/postgresql:/var/run/postgresql:z [Service] Restart=always @@ -59,8 +59,5 @@ TimeoutStopSec=30 # These environment variables are sourced to be used by systemd in the Exec* commands EnvironmentFile=/etc/quadlets/postgresql/config.env -# Skaffold filesystem + fix permissions -ExecStartPre=install -m 0700 -o 70 -g 70 -d /var/run/quadlets/postgresql - [Install] WantedBy=postgresql.target diff --git a/postgresql/postgresql.target b/postgresql/postgresql.target index 55f9f2b..846a5d9 100644 --- a/postgresql/postgresql.target +++ b/postgresql/postgresql.target @@ -1,8 +1,9 @@ [Unit] Description=PostgreSQL Service Target Documentation=man:systemd.target(5) -Requires=postgresql-server.service postgresql-upgrade.service postgresql-init.service postgresql-set-major.service +Requires=postgresql-server.service postgresql-upgrade.service postgresql-init.service postgresql-set-major.service postgresql-backup.timer After=postgresql-server.service postgresql-upgrade.service postgresql-init.service postgresql-set-major.service +Before=postgresql-backup.timer # Allow isolation - can stop/start this target independently AllowIsolate=yes diff --git a/postgresql/sysctl.d/postgresql.conf b/postgresql/sysctl.d/postgresql.conf new file mode 100644 index 0000000..38c0eec --- /dev/null +++ b/postgresql/sysctl.d/postgresql.conf @@ -0,0 +1,3 @@ +# PostgreSQL recommended settings for stability +#vm.overcommit_memory=2 +#vm.overcommit_ratio=80 diff --git a/postgresql/tmpfiles.d/postgresql.conf b/postgresql/tmpfiles.d/postgresql.conf new file mode 100644 index 0000000..8bba2ea --- /dev/null +++ b/postgresql/tmpfiles.d/postgresql.conf @@ -0,0 +1 @@ +d$ /run/quadlets/postgresql 0700 70 70 -