A Script for Backing Up Nextcloud to an FTP Server

#!/bin/bash

# ==== CONFIGURATION ====
NEXTCLOUD_PATH="/var/www/html"
DATA_PATH="/var/www/html/data"
BACKUP_PATH="/var/backups/nextcloud"

DB_USER="nextcloud"
DB_PASS="dbpassword"
DB_NAME="nextcloud"

FTP_HOST="yourftphost.com"
FTP_USER="yourftpuser"
FTP_PASS="yourftppassword"
FTP_DIR="/path/to/save/file"

DATE=$(date +"%Y-%m-%d_%H-%M")
ARCHIVE_NAME="nextcloud-backup-$DATE.tar.gz"

MAX_LOCAL_BACKUPS=3

# ==== START BACKUP ====
echo "Starting streamed Nextcloud backup..."

mkdir -p "$BACKUP_PATH"

# Export database (small file)
DB_DUMP="/tmp/db_$DATE.sql"
mysqldump --no-tablespaces -u "$DB_USER" -p"$DB_PASS" "$DB_NAME" > "$DB_DUMP"

# === Stream tar directly to FTP ===
echo "Creating and streaming archive directly to FTP..."

tar -czf - \
--exclude="$DATA_PATH/appdata_*/preview" \
--exclude="$NEXTCLOUD_PATH/updater-*" \
--exclude="$NEXTCLOUD_PATH/data/tmp" \
-C / \
"${NEXTCLOUD_PATH#/}" \
"${DATA_PATH#/}" \
"$DB_DUMP" \
| curl --ftp-pasv -T - "ftp://$FTP_HOST$FTP_DIR/$ARCHIVE_NAME" --user "$FTP_USER:$FTP_PASS"

# Remove temp database dump
rm -f "$DB_DUMP"

# Optional: keep a few local backups (not needed for streamed uploads)
echo "Cleaning up old local backups..."
cd "$BACKUP_PATH" || exit
ls -1t nextcloud-backup-*.tar.gz 2>/dev/null | tail -n +$((MAX_LOCAL_BACKUPS + 1)) | xargs -r rm -f --

echo "Backup complete: streamed directly to FTP as $ARCHIVE_NAME"

Setting up a NAS on a Raspberry Pi 5 that boots from NVME

# curl https://download.argon40.com/argonneo5.sh | bash
# nano /boot/firmware/config.txt

kernel=kernel8.img
dtparam=nvme
dtparam=pciex1_gen=3
usb_max_current_enable=1

# sudo apt update
# sudo apt install snapd
# sudo snap install snapd
# sudo snap install nextcloud
# sudo /snap/bin/nextcloud.enable-https lets-encrypt
# sudo /snap/bin/nextcloud.occ maintenance:repair --include-expensive
# sudo /snap/bin/nextcloud.occ background:cron
# apt install syncthing
# systemctl enable syncthing@pi-nas.service
# systemctl start syncthing@pi-nas.service
# nano /usr/lib/systemd/system/syncthing@.service

Group=users
UMask=0002

# apt install proftpd
# nano /etc/proftpd/proftpd.conf

DefaultRoot ~
Umask 002 002

# nano /usr/local/bin/fix_ownership.sh

#!/bin/bash
WATCHDIR="/volume1"
LOGFILE="/var/log/fix_ownership.log"

# Function to fix ownership of a file or directory
fix_ownership() {
    local file="$1"
    if [ -e "$file" ]; then
        if [ -d "$file" ]; then
            chown -R pi-nas:users "$file" 2>/dev/null
        else
            chown pi-nas:users "$file" 2>/dev/null
        fi
        echo "$(date '+%Y-%m-%d %H:%M:%S') Fixed ownership: $file" >> "$LOGFILE"
    fi
}

# Watch for changes
inotifywait -m -r -e close_write,create,move,delete --format '%w%f' "$WATCHDIR" | while read -r file; do
    fix_ownership "$file"
done

# nano /etc/systemd/system/fix_ownership.service

[Unit]
Description=Recursive ownership watcher for Volume1 folder
After=network.target

[Service]
ExecStart=/usr/local/bin/fix_ownership.sh
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

# systemctl daemon-reload
# systemctl enable fix_ownership.service
# systemctl start fix_ownership.service

Setting Up Radicale and Infcloud in Debian 13 (Trixie)

# apt update
# apt install git python3-pip python3-venv
# useradd -r -m -d /var/lib/radicale -s /bin/bash radicale
# mkdir /etc/radicale
# nano /etc/radicale/radicale.conf

[server]
# Bind to all IP addresses (0.0.0.0) instead of just localhost
hosts = 0.0.0.0:5232
[auth]
type = htpasswd
htpasswd_filename = /etc/radicale/users
htpasswd_encryption = bcrypt
[storage]
filesystem_folder = /var/lib/radicale/collections
[web]
type = radicale_infcloud

# htpasswd -c -B /etc/radicale/users yourusername
# htpasswd -B /etc/radicale/users anotheruser
# ufw allow 5232/tcp
# ufw reload
# su radicale
$ cd ~
$ python3 -m venv venv
$ source venv/bin/activate
$ pip install radicale bcrypt git+https://github.com/Unrud/RadicaleInfCloud.git
$ radicale --config /etc/radicale/radicale.conf

http://your-server-ip:5232/

$ exit
# nano /etc/systemd/system/radicale.service

[Unit]
Description=Radicale CalDAV and CardDAV Server
After=network.target
[Service]
User=radicale
Group=radicale
ExecStart=/var/lib/radicale/venv/bin/radicale --config /etc/radicale/radicale.conf
WorkingDirectory=/var/lib/radicale
Restart=on-failure
LimitNOFILE=4096
[Install]
WantedBy=multi-user.target

# systemctl daemon-reload
# systemctl enable radicale
# systemctl start radicale
# systemctl status radicale

# apt install apache2 certbot python3-certbot-apache
# nano /etc/apache2/sites-available/radicale.conf

<VirtualHost *:80>
  ServerName your_domain.com
  ProxyPass / http://localhost:5232/
  ProxyPassReverse / http://localhost:5232/
</VirtualHost>

# ufw allow 80/tcp
# ufw allow 443/tcp
# ufw reload
# certbot --apache -d your_domain.com

https://your_domain.com

# sudo systemctl status certbot.timer
# sudo certbot renew --dry-run