#!/usr/bin/env bash # Nextcloud + Apache + PHP 8.3-FPM on Ubuntu 24.04 # - Applies requested php.ini edits # - Apache with proxy_fcgi (no mod_php) # - Downloads Nextcloud to /var/www/nextcloud # - MariaDB DB + user # - Redis for file locking (optional but recommended) # - Cloudflare Tunnel / reverse-proxy aware (HTTPS forced, trusted proxy, real client IP) set -euo pipefail IFS=$'\n\t' export DEBIAN_FRONTEND=noninteractive # ====== EDIT ME ====== DOMAIN="domain.com" NC_DIR="/var/www/nextcloud" NC_DATA_DIR="/var/ncdata" DB_NAME="nextcloud" DB_USER="anon" DB_PASS="changeMe" ADMIN_USER="anon" ADMIN_PASS="changeMe" PHP_VER="8.3" PHP_FPM_SOCK="/run/php/php${PHP_VER}-fpm.sock" # ====== Base packages ====== apt-get update apt-get -y upgrade apt-get install -y software-properties-common apt-transport-https ca-certificates \ lsb-release unzip wget curl gnupg2 tar sudo # ====== MariaDB ====== apt-get install -y mariadb-server mariadb-client systemctl enable --now mariadb # Headless mysql_secure_installation equivalent mysql --protocol=socket <<'SQL' DELETE FROM mysql.user WHERE User=''; DROP DATABASE IF EXISTS test; DELETE FROM mysql.db WHERE Db='test' OR Db LIKE 'test\_%'; FLUSH PRIVILEGES; SQL mysql --protocol=socket < PHP-FPM a2dismod mpm_prefork >/dev/null 2>&1 || true a2enmod mpm_event >/dev/null 2>&1 || true # (Optional) If you want Apache logs to show real client IPs with Cloudflare: a2enmod remoteip >/dev/null 2>&1 || true cat >/etc/apache2/conf-available/remoteip-cloudflare.conf <<'EOF' # Trust localhost (cloudflared) as the proxy and use CF's connecting IP header RemoteIPHeader CF-Connecting-IP # Since cloudflared connects from 127.0.0.1, mark it as trusted RemoteIPTrustedProxy 127.0.0.1 RemoteIPTrustedProxy ::1 EOF a2enconf remoteip-cloudflare >/dev/null 2>&1 || true # Nextcloud vhost (HTTP only; TLS is terminated by Cloudflare Tunnel) cat >/etc/apache2/sites-available/nextcloud.conf < ServerName ${DOMAIN} DocumentRoot ${NC_DIR} Require all granted AllowOverride All Options FollowSymLinks MultiViews Dav off # Large uploads LimitRequestBody 0 SetEnv HOME ${NC_DIR} SetEnv HTTP_HOME ${NC_DIR} # Security headers (add HSTS at the edge in Cloudflare if desired) Header always set Referrer-Policy "no-referrer" Header always set X-Content-Type-Options "nosniff" Header always set X-Frame-Options "SAMEORIGIN" Header always set X-XSS-Protection "1; mode=block" # PHP-FPM socket SetHandler "proxy:unix:${PHP_FPM_SOCK}|fcgi://localhost/" ErrorLog \${APACHE_LOG_DIR}/nextcloud_error.log CustomLog \${APACHE_LOG_DIR}/nextcloud_access.log combined EOF a2ensite nextcloud.conf a2dissite 000-default.conf >/dev/null 2>&1 || true systemctl enable --now apache2 apache2ctl configtest systemctl reload apache2 # ====== Download Nextcloud (latest) ====== mkdir -p /tmp/nc cd /tmp/nc wget -q https://download.nextcloud.com/server/releases/latest.zip unzip -q -o latest.zip # Deploy to /var/www/nextcloud rm -rf "${NC_DIR}" mv nextcloud "${NC_DIR}" # Ownership & permissions install -d -m 0750 "${NC_DATA_DIR}" chown -R www-data:www-data "${NC_DIR}" "${NC_DATA_DIR}" chmod -R 750 "${NC_DIR}" # ====== Bootstrap Nextcloud (non-interactive) ====== sudo -u www-data php "${NC_DIR}/occ" maintenance:install \ --database "mysql" \ --database-name "${DB_NAME}" \ --database-user "${DB_USER}" \ --database-pass "${DB_PASS}" \ --admin-user "${ADMIN_USER}" \ --admin-pass "${ADMIN_PASS}" \ --data-dir "${NC_DATA_DIR}" # ====== Reverse-proxy / Cloudflare Tunnel awareness ====== # Use HTTPS in public URL and force HTTPS (fixes OIDC "must access via HTTPS") sudo -u www-data php "${NC_DIR}/occ" config:system:set overwrite.cli.url --value="https://${DOMAIN}" sudo -u www-data php "${NC_DIR}/occ" config:system:set overwriteprotocol --value="https" # Trust the local proxy (cloudflared connects from localhost) sudo -u www-data php "${NC_DIR}/occ" config:system:set trusted_proxies 0 --value="127.0.0.1" sudo -u www-data php "${NC_DIR}/occ" config:system:set trusted_proxies 1 --value="::1" # Record Cloudflare's real client IP header inside Nextcloud sudo -u www-data php "${NC_DIR}/occ" config:system:set forwarded_for_headers 0 --value="HTTP_CF_CONNECTING_IP" # Trusted domain sudo -u www-data php "${NC_DIR}/occ" config:system:set trusted_domains 1 --value="${DOMAIN}" # Caching: APCu local + Redis locking (unix socket) sudo -u www-data php "${NC_DIR}/occ" config:system:set memcache.local --value='\OC\Memcache\APCu' sudo -u www-data php "${NC_DIR}/occ" config:system:set memcache.locking --value='\OC\Memcache\Redis' sudo -u www-data php "${NC_DIR}/occ" config:system:set redis --type=json --value='{"host":"\/var\/run\/redis\/redis-server.sock","port":0,"timeout":1.5}' # ====== Cron every 5 minutes ====== cat >/etc/cron.d/nextcloud <