Nextcloud Installing Nextcloud manually. No turnkey Linux, no community script. We install Nextcloud manually. Step by step. Just like they used to do it in the old days. So you can understand why certain things work the way they do. You'll also learn something about Linux, which is a good thing. Part 1: Creating the VM and Installing Debian 13 Objective of This Section In this first part, we create a virtual machine in Proxmox and install a minimal Debian 13 system on it. At the end of this section: Debian is running successfully inside the VM the system is accessible via console we are ready for network, storage, and Nextcloud setup Prerequisites Hardware Recommendations RAM: at least 4 GB (recommended: 8 GB) System Disk: at least 32 GB (SSD/NVMe recommended) Data Disk: separate HDD or SSD for Nextcloud data CPU: 2–4 cores VM Parameters (Example) VM ID: 120 vCPUs: 4 RAM: 4 GB Root Disk: 32 GB Data Disk: 200 GB (later 250 GB) Mount Target (later): /srv/cloud.zn80.net/data Note: The data disk is not used during the Debian installation, but will be mounted separately at a later stage. Required Credentials (Example) ⚠️ Note: In a production environment, use secure, unique passwords and store them in a password manager. root (VM) ncadmin (VM) Database User: nextcloud Nextcloud Admin: nextcloud_admin Debian Installation Medium We use Debian 13 (amd64). Download source (ISO):  https://debian.snt.utwente.nl/debian-cd/13.3.0/amd64/iso-cd/   Step 1: Create the VM in Proxmox Create a new VM Assign the desired VM ID Select the previously downloaded Debian ISO Step 2: VM System Configuration Machine Type and Agent Machine: q35 → modern CPU features such as improved PCIe, NVMe, and hardware emulation support QEMU Guest Agent: enable → improved status reporting and clean shutdown functionality Why q35 instead of i440fx : The q35 machine type emulates a modern Intel chipset with native PCIe support. This enables proper PCIe passthrough (e.g., for GPUs or NVMe devices), AHCI controllers, and vIOMMU emulation. Compared to the legacy i440fx , it is significantly better suited for modern guest systems and extensions. QEMU Guest Agent: The QEMU Guest Agent is a small daemon running inside the guest system that enables direct communication with Proxmox. It allows clean shutdowns (instead of ACPI force-off), freezes file systems before snapshots/backups (ensuring consistency), and provides accurate status information such as IP addresses and memory usage. Configure the System Disk Disk Size: 32 GB Cache: Default Discard: Enable (only relevant for SSD/NVMe) Add Second Disk (Data) We now add a second disk , which will later be used exclusively for Nextcloud data. Size: e.g., 200 GB Storage: according to your infrastructure Discard: enable (for SSD/NVMe) We deliberately start the data disk at 200 GB. It can later be expanded easily in Proxmox and within the guest system to demonstrate dynamic storage growth in a homelab environment. CPU Configuration Cores: 4 (2 are sufficient for small setups) Type: host → exposes all CPU features to the VM Why host : The CPU type host passes all features and extensions of the physical host CPU (e.g., AVX, AES-NI) directly to the VM. This provides maximum performance. However, it is only recommended if you do not plan live migration between hosts with different CPU types, as this can cause migration failures. Memory Minimum: 2 GB (functional) Recommended: 4 GB Network The default network configuration can remain unchanged. Review Configuration If everything is configured correctly, the VM summary should look similar to this. Now start the VM and begin the Debian installation. Step 3: Install Debian Language & Region To select Germany : Other Europe Germany The keyboard layout can then be accepted as proposed. Hostname & Domain Hostname: e.g., nc or cloud Domain: either public or internal (e.g., intranet.zuhause.de or example.org ) In this example, I chose: cloud zn80.net Set Root Password Assign a strong root password. Since direct SSH login as root poses a security risk, we will disable this capability after installation. Create a Regular User In addition to the root user, we require a regular user account. In this example, the username is ncadmin . Assign a strong password here as well. Partitioning Guided – use entire disk Important: select the small 32 GB disk (usually /dev/sda ) All files in one partition Then write the changes to disk. Partitioning Notes During Debian installation, the commonly selected option is “Guided – use entire disk.” An alternative is: “Guided – use entire disk and set up LVM.” This creates Logical Volume Manager (LVM) volumes, allowing more flexible resizing of partitions (especially the root filesystem) later without repartitioning the disk. Swap Area Regardless of the partitioning method, a swap partition or swap file should be configured. In the screenshot above, a 1.8 GB swap partition is shown. Typical guidelines: At least 2–4 GB swap for low-RAM systems For systems with ≤ 8 GB RAM, swap equal to RAM (1:1) is often recommended With large amounts of RAM (> 16–32 GB), significantly less swap (2–8 GB) or a swap file is usually sufficient Package Sources & Updates Additional media: No Debian mirror: Country: Germany Mirror: deb.debian.org Proxy: No Participate in package usage survey: No Software Selection We install no desktop environment . ❌ Debian Desktop Environment ❌ GNOME ✅ SSH Server The web server will be installed manually later. Bootloader Install GRUB to: /dev/sda (SCSI-0) The Debian installation is now complete. Step 4: Verify Boot Order Remove the ISO from the VM’s CD/DVD drive and set the boot order to prioritize the hard disk. Otherwise, the VM may continuously attempt to boot from the empty ISO. If the VM boots from the ISO again: Power off the VM Options → Boot Order Move the system disk to the top Move the ISO down or disable it Boot order incorrect Boot order correct Result If everything was completed correctly, you should now see the Debian 13 login console. Next Step Continue with: Part 2: Configure IP, User Permissions, and Second Data Disk Configure a static IP Partition, format, and mount the second disk Prepare the base system Part 2: Configuring IP, User Privileges, and the Second Data Disk Objective of This Section In this section, we further configure the freshly installed Debian VM. We assign a static IP address, establish proper user privileges, and permanently mount the second disk for Nextcloud data. All packages will be updated to the latest state, and clean administrative privileges will be prepared. At the end of this section: the VM is reachable via a static IP address a regular admin user with sudo privileges is configured the data disk is correctly mounted and persistently integrated Step 1: Log In to the VM and Initial Overview Prerequisites Debian 13 has been successfully installed The VM is running in Proxmox Access to the Proxmox web interface An SSH client (e.g., Tabby, Terminal, iTerm, PuTTY) Hardware Recommendations The second virtual disk has already been added in Proxmox Size as required (e.g., 200 GB or more) The disk has not yet been configured inside Debian VM Parameters (Example) Hostname: nextcloud-vm Static IP (example): 192.168.10.120 Gateway: 192.168.10.1 DNS: internal DNS + fallback Required Credentials (Example) User: ncadmin Root password SSH access to the VM Log In via SSH Start the VM in Proxmox and determine the currently assigned IP address. Then connect via SSH using the admin user ncadmin (or the user created in Part 1): # Use your VM’s name and IP address ssh ncadmin@192.168.10.120 On first connection, confirm the fingerprint with yes , then enter the password. Step 2: Configure a Static IP Address Objective The VM must always be reachable under the same IP address. This is a prerequisite for DNS configuration, reverse proxy usage, and later for Nextcloud itself. Edit Network Configuration Open the file: /etc/network/interfaces : sudo nano /etc/network/interfaces Modify the primary interface as follows (the interface name may differ, often ens18 ): # The primary network interface allow-hotplug ens18 #iface ens18 inet dhcp iface ens18 inet static address 192.168.10.120 netmask 255.255.255.0 gateway 192.168.10.1 dns-nameservers 192.168.10.6 1.1.1.1 Adjust the IP addresses to match your network. Save with CTRL+O , confirm with Enter, exit with CTRL+X . To ensure that the dns-nameservers defined in the interfaces file are actually applied, the resolvconf package must be installed and enabled: sudo apt install resolvconf sudo systemctl enable resolvconf sudo systemctl start resolvconf Restart networking: sudo systemctl restart networking Important: Do not close the current terminal. Instead, open a new terminal and test whether SSH login works again. Connection successful → configuration is correct Connection fails → use the existing terminal session to correct the network configuration Step 3: Update the System and Root Access Switch to Root Mode su - Update the System apt update apt full-upgrade This updates both the package lists and all installed packages. Optionally, the hostname can also be adjusted at this stage. Step 4: Configure sudo Privileges The regular user should be able to perform administrative tasks without remaining logged in as root. Install sudo apt install sudo Add User to sudo Group usermod -aG sudo ncadmin Afterward, log out from root. Future administrative commands can be executed using sudo . Step 5: Prepare the Second Disk The second virtual disk will be partitioned, formatted, and mounted as the data directory for Nextcloud. ⚠️ The mount point /srv/cloud.zn80.net is only an example. You may use any suitable path, such as /srv/nextcloud/data , /var/lib/nextcloud , or /home/ncadmin/cloud-data . Choose a structure that aligns with your system design and remains intuitive. Overview of Available Drives lsblk The second disk is usually /dev/sdb . Create Partition sudo fdisk /dev/sdb Within fdisk , enter: n (new) p (primary) 1 Enter (start) Enter (end) w (write) Create Filesystem sudo mkfs.ext4 -L cloud.zn80.net /dev/sdb1 Create Mount Directory sudo mkdir -p /srv/cloud.zn80.net Step 6: Permanently Mount the Disk Objective The data disk should automatically mount at system startup. Determine UUID blkid Note the UUID of /dev/sdb1 . The UUID is important because device names may change after reboot, whereas UUIDs remain constant. Edit /etc/fstab sudo nano /etc/fstab Add or modify the following line: # nextcloud data UUID=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX /srv/cloud.zn80.net ext4 defaults,noatime 0 2 Save and close the editor. Test Mount sudo mount -a df -h The disk should now be mounted under /srv/cloud.zn80.net . The system is now fully prepared. The actual Nextcloud installation follows in: Part 3: Downloading, Installing, and Configuring Nextcloud Step 7: Resize the Disk (Optional) ⚠️ Important Notice Before Resizing Before resizing the data disk, create a backup of the VM or critical data. A mistake (e.g., selecting the wrong partition) can result in complete data loss. The VM must be powered off and on again for the rescan and parted steps. Live resizing using resize2fs works only with ext4 and a mounted filesystem, but it is riskier and not always recommended. Proceed carefully and verify the partition before modifying it. Objective If additional storage is required later, the existing partition can be expanded without data loss. Resize Disk in Proxmox Select VM Disk → Resize Add desired capacity Rescan in Debian echo 1 > /sys/block/sdb/device/rescan Reboot the VM if necessary. Check Free Space parted -s -a opt /dev/sdb "print free" Resize Partition parted -s -a opt /dev/sdb "resizepart 1 100%" Resize Filesystem e2fsck -f /dev/sdb1 resize2fs /dev/sdb1 Afterward, lsblk and df -h will reflect the new size. Note: Differences in displayed sizes are due to GB vs. GiB. Teil 3: Downloading, installing and configuring Nextcloud Objective of This Section In this section, we install Nextcloud completely on the prepared Debian 13 system. We set up the LAMP stack, configure Apache and PHP specifically for Nextcloud, integrate the data directory outside the webroot, and complete the installation including essential optimizations. At the end of this section: Nextcloud is running under cloud.zn80.net All user data is stored cleanly under /srv/cloud.zn80.net/data The database is secured and properly connected Caching, proxy operation, and optional performance features are prepared Important Security Notice For secure operation on the public internet, a reverse proxy with HTTPS is mandatory (e.g., Nginx Proxy Manager, Traefik, or a standalone Nginx setup with Let’s Encrypt). Without HTTPS (and HSTS), it is strongly discouraged to expose the Nextcloud instance beyond the local network. You may operate it internally first and enable external access later with a proxy and certificate at any time. Further information: Nextcloud Admin Documentation: HTTPS Setup & Server Hardening Nextcloud Admin Documentation: SSL/TLS Configuration Let’s Encrypt Official Website Update the System Before beginning the installation, ensure the system is fully up to date: sudo apt update && sudo apt full-upgrade -y Install and Secure MariaDB Install the database server and client: sudo apt install mariadb-server mariadb-client-compat Enter the MariaDB shell and create the Nextcloud database: sudo mariadb Inside the MariaDB shell: CREATE DATABASE nextcloud; GRANT ALL PRIVILEGES ON nextcloud.* TO 'nextcloud'@'localhost' IDENTIFIED BY ''; FLUSH PRIVILEGES; EXIT; Secure the MariaDB installation: sudo mysql_secure_installation Recommended answers: unix_socket authentication: n Set root password: Y Remove anonymous users: Y Disallow remote root login: Y Remove test database: Y Reload privilege tables: Y Install Apache, PHP, and Required Extensions Install Apache indirectly via PHP packages along with all required extensions: sudo apt install imagemagick-7.q16 php php-apcu php-bcmath php-cli php-common php-curl php-gd php-gmp php-imagick php-intl php-mbstring php-mysql php-zip php-bz2 php-xml Enable required PHP modules: sudo phpenmod apcu bcmath gmp imagick intl unzip Ensure unzip and wget are installed: sudo apt install unzip wget Download and Prepare Nextcloud Download and extract the latest Nextcloud version: wget https://download.nextcloud.com/server/releases/latest.zip unzip latest.zip Rename the extracted directory to the service FQDN and move it into Apache’s web directory: mv nextcloud cloud.zn80.net sudo chown -R www-data:www-data cloud.zn80.net sudo mv cloud.zn80.net /var/www/ Disable the Apache default site: sudo a2dissite 000-default.conf Enable required Apache modules: sudo a2enmod dir env headers mime rewrite ssl sudo systemctl restart apache2 Note: Instead of latest.zip , specific previous versions (e.g., nextcloud-29.zip or nextcloud-30.zip ) can be downloaded from the official release archive if required for compatibility reasons. However, running outdated versions on the public internet is not recommended. Create Apache Virtual Host for Nextcloud Create the configuration file: sudo nano /etc/apache2/sites-available/cloud.zn80.net.conf Insert the following content: ServerAdmin mail@example.com DocumentRoot "/var/www/cloud.zn80.net" ServerName cloud.zn80.net Options FollowSymLinks AllowOverride All Header always set Referrer-Policy "no-referrer" Require all granted SetEnv HOME /var/www/cloud.zn80.net SetEnv HTTP_HOME /var/www/cloud.zn80.net TransferLog /var/log/apache2/cloud.zn80.net_access.log ErrorLog /var/log/apache2/cloud.zn80.net_error.log Do not enable the site yet. Optimize PHP for Nextcloud Edit PHP configuration: sudo nano /etc/php/8.4/apache2/php.ini Adjust the following values: memory_limit = 512M upload_max_filesize = 200M post_max_size = 200M max_execution_time = 360 date.timezone = Europe/Amsterdam opcache.enable=1 opcache.memory_consumption=128 opcache.interned_strings_buffer=16 opcache.max_accelerated_files=10000 opcache.revalidate_freq=1 opcache.save_comments=1 Enable the site and restart Apache: sudo a2ensite cloud.zn80.net.conf sudo systemctl restart apache2 Enable APCu for CLI: sudo nano /etc/php/8.4/mods-available/apcu.ini Append: apc.enable_cli=1 Restart Apache again: sudo systemctl restart apache2 Prepare the Data Directory The Nextcloud data directory must not be located inside the webroot. Ensure the mount point exists: sudo mkdir -p /srv/cloud.zn80.net Test mounting: sudo mount -a df -h /srv/cloud.zn80.net Create the actual data directory and apply secure permissions: sudo mkdir /srv/cloud.zn80.net/data sudo chown -R www-data:www-data /srv/cloud.zn80.net/data sudo chmod -R 750 /srv/cloud.zn80.net/data Web-Based Nextcloud Installation Open in your browser: http://cloud.zn80.net Provide the following: Admin user: Username: nextcloud_admin Password: secure custom password Data directory: /srv/cloud.zn80.net/data Database: User: nextcloud Database: nextcloud Password: database password Server: localhost:3306 After installation, return to the command line. Post-Installation Optimizations Optimize the Nextcloud Database Temporarily make the occ CLI tool executable: sudo chmod +x /var/www/cloud.zn80.net/occ Add missing database indices: sudo /var/www/cloud.zn80.net/occ db:add-missing-indices Run repair tasks: sudo /var/www/cloud.zn80.net/occ maintenance:repair --include-expensive Remove executable permission again: sudo chmod -x /var/www/cloud.zn80.net/occ Secure configuration file: sudo chown root:www-data /var/www/cloud.zn80.net/config/config.php sudo chmod 660 /var/www/cloud.zn80.net/config/config.php Configure Trusted Proxies, Domains, and Caching Edit configuration file: sudo nano /var/www/cloud.zn80.net/config/config.php Add: 'trusted_proxies' => [ '192.168.10.105', ], 'trusted_domains' => [ 'cloud.zn80.net', '192.168.10.120', ], 'overwrite.cli.url' => 'https://cloud.zn80.net', 'overwriteprotocol' => 'https', 'memcache.local' => '\\OC\\Memcache\\APCu', 'default_phone_region' => 'DE', Run repair command: sudo -u www-data php occ maintenance:repair Install Redis (Optional, Recommended) Install: sudo apt install redis-server php-redis Add configuration in config.php : 'filelocking.enabled' => true, 'memcache.locking' => '\\OC\\Memcache\\Redis', 'redis' => [ 'host' => '/run/redis/redis-server.sock', 'port' => 0, 'timeout' => 0.0, ], Restart services: sudo systemctl restart redis sudo systemctl restart apache2 The Nextcloud installation is now complete and properly configured. In Part 4, additional security hardening and recommended configuration adjustments will be implemented. Part 4: Recommended Security Settings and System Hardening Below are several items you should review again after completing the setup. You may already have configured many of them, but a second verification is always advisable. Update the System and Install Base Packages After the first boot, log in as root or ncadmin (depending on console or SSH access) and immediately run: apt update && apt full-upgrade -y This installs all security updates and bug fixes released since the ISO was built. Then install useful base utilities: apt install sudo vim htop curl wget net-tools -y This ensures sudo availability for regular users and provides improved system and networking tools. Harden SSH – Disable Password Login and Use Keys SSH is enabled during installation but not yet hardened. Edit: sudo nano /etc/ssh/sshd_config Set: PermitRootLogin prohibit-password PasswordAuthentication no Generate an SSH key on your client: ssh-keygen Copy the public key to the server: ssh-copy-id ncadmin@your-vm-ip Restart SSH: systemctl restart ssh From this point forward, only key-based authentication is allowed, effectively preventing password brute-force attacks. Configure Cron for Nextcloud Nextcloud requires a recurring background job (file scanning, mail delivery, app updates, etc.). Create a cron job: crontab -e Add: */5 * * * * php -f /var/www/cloud.zn80.net/cron.php Alternatively, use a systemd timer. Without cron, background tasks run only during active web sessions, which can cause delays or stuck jobs. Note: Normally, the cron job is created with: crontab -u www-data -e This may not work in this setup if the ownership of the configuration file has been changed earlier. Install Fail2Ban Against Brute-Force Attacks Install Fail2Ban: apt install fail2ban -y Enable and start it: systemctl enable --now fail2ban Fail2Ban monitors SSH logs and bans IP addresses after multiple failed login attempts (default: 5 attempts within 10 minutes). The Debian defaults are typically sufficient for a homelab environment. Optional: Adjust /etc/fail2ban/jail.local for longer ban durations or email notifications. .htaccess Optimization and Security Headers After installation, update the .htaccess file: sudo -u www-data php occ maintenance:repair Add security headers in the Apache VirtualHost configuration (preferably under *:443 ) or in your reverse proxy configuration: Header always set X-Content-Type-Options "nosniff" Header always set X-Frame-Options "SAMEORIGIN" Header always set X-XSS-Protection "1; mode=block" Header always set Referrer-Policy "strict-origin-when-cross-origin" If running behind Nginx Proxy Manager, add the following under the proxy host’s custom configuration: add_header X-Content-Type-Options "nosniff" always; add_header X-Frame-Options "SAMEORIGIN" always; add_header X-XSS-Protection "1; mode=block" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; These headers mitigate: MIME sniffing attacks ( nosniff ) Clickjacking ( SAMEORIGIN ) Reflected XSS ( mode=block ) Unnecessary referrer exposure ( strict-origin-when-cross-origin ) Enable HSTS (Strict-Transport-Security) Within the proxy host SSL settings, enable: Force SSL HSTS Enabled HSTS Subdomains (if applicable) Nextcloud expects at least: max-age=15552000 If necessary, manually add: add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always; Important: Only enable HSTS if HTTPS is permanently available. Additional Reverse Proxy Adjustments (Optional) To prevent header conflicts or WebSocket upgrade issues: proxy_hide_header Upgrade; proxy_hide_header Connection; For additional protection: add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always; After saving, test the Nextcloud instance (Admin → Overview / Security scan). Adjust Data Directory Permissions The data directory /srv/cloud.zn80.net/data should preferably use: chmod 770 /srv/cloud.zn80.net/data chown -R www-data:www-data /srv/cloud.zn80.net/data While 750 may work, 770 is recommended to prevent permission issues during app installation or large uploads. Enable Firewall with UFW If the server is directly exposed to the internet, install UFW: apt install ufw -y Allow necessary ports: ufw allow OpenSSH ufw allow 80,443/tcp Enable and verify: ufw enable ufw status verbose UFW blocks all other ports by default. Additional Security Recommendations Use a dedicated sudo-capable user and avoid permanent root login. Enable automatic security updates: apt install unattended-upgrades -y Review configuration in: /etc/apt/apt.conf.d/50unattended-upgrades Check for updates regularly: apt list --upgradable With these measures in place, your Nextcloud instance should achieve strong security ratings (e.g., A/A+ on securityheaders.com or Mozilla Observatory) and display no major warnings in the Nextcloud admin security overview.