{"id":1053,"date":"2026-09-06T16:49:29","date_gmt":"2026-09-06T14:49:29","guid":{"rendered":"https:\/\/gentz.cloud\/?p=1053"},"modified":"2026-09-06T16:51:37","modified_gmt":"2026-09-06T14:51:37","slug":"replacing-qnap-qts-with-openmediavault","status":"publish","type":"post","link":"https:\/\/gentz.cloud\/?p=1053","title":{"rendered":"Replacing QNAP QTS with OpenMediaVault"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">My QNAP TS-469L is now around 13 years old. From a hardware perspective, that sounds ancient, especially when compared with modern NAS systems. Nevertheless, the device is still perfectly usable for what I need: network storage, RAID, SMB shares and backups.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Instead of replacing perfectly functional hardware, I decided to give the TS-469L a second life by replacing QNAP&#8217;s operating system with <strong>OpenMediaVault (OMV)<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The result was surprisingly straightforward \u2014 and the performance is better than I expected from hardware of this age.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This article describes how I installed OMV, rebuilt the storage, restored roughly 3.5 TB of data and finally added an automated weekly OneDrive backup using rclone.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><a href=\"https:\/\/gentz.cloud\/wp-content\/uploads\/2026\/09\/image.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"723\" src=\"https:\/\/gentz.cloud\/wp-content\/uploads\/2026\/09\/image-1024x723.png\" alt=\"\" class=\"wp-image-1055\" srcset=\"https:\/\/gentz.cloud\/wp-content\/uploads\/2026\/09\/image-1024x723.png 1024w, https:\/\/gentz.cloud\/wp-content\/uploads\/2026\/09\/image-300x212.png 300w, https:\/\/gentz.cloud\/wp-content\/uploads\/2026\/09\/image-767x542.png 767w, https:\/\/gentz.cloud\/wp-content\/uploads\/2026\/09\/image.png 1274w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n<\/div>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">The Hardware<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">My system is a:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>QNAP TS-469L<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The important difference compared with a standard OMV installation is that I did not want to install the operating system onto one of the internal storage disks.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Instead, I bought a small SSD and connected it externally to one of the QNAP&#8217;s USB 2.0 ports.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The basic setup therefore looks like this:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nQNAP TS-469L\n\u2502\n\u251c\u2500\u2500 External USB SSD\n\u2502   \u2514\u2500\u2500 OpenMediaVault \/ Debian\n\u2502\n\u251c\u2500\u2500 Internal USB Memory\n\u2502   \u2514\u2500\u2500 QTS OS\n\u2502\n\u2514\u2500\u2500 Internal HDDs\n    \u2514\u2500\u2500 RAID1 mirror\n    \u2514\u2500\u2500 1TB HDD\n    \u2514\u2500\u2500 3 TB HDD\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">Using USB 2.0 for the operating system obviously isn&#8217;t particularly fast by today&#8217;s standards, but OMV itself does not generate huge amounts of disk I\/O during normal NAS operation.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For my use case, this works perfectly well.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Preparation: Back Up Everything First<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Before touching the NAS, I copied approximately <strong>3.5 TB of existing data to external storage<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This was probably the most important preparation step.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I did not want to preserve the existing QNAP disk configuration. My intention was to completely reinitialize the disks after installing OMV and create a clean storage configuration.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">That meant I needed to assume that everything on the internal disks would be lost.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">My approach was therefore:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nQNAP data\n     \u2502\n     \u25bc\nExternal backup (~3.5 TB)\n     \u2502\n     \u25bc\nInstall OpenMediaVault\n     \u2502\n     \u25bc\nReinitialize internal disks\n     \u2502\n     \u25bc\nCreate new storage \/ RAID\n     \u2502\n     \u25bc\nRestore data\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">I would strongly recommend doing the same.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Even if you intend to reuse an existing filesystem, a full independent backup before replacing the NAS operating system is a very good idea.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Creating the OpenMediaVault Installation USB Stick<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">For the installation itself, I used the ready-made OpenMediaVault appliance image and wrote it to a USB flash drive.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The QNAP was then booted from this USB stick.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I had another USB device connected at the same time: the SSD that would become the permanent OMV system disk.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">So during installation there were essentially three categories of storage:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nInstallation USB stick\n        \u2502\n        \u251c\u2500\u2500 OMV installer\n        \u2502\n        \u25bc\nQNAP TS-469L\n        \u2502\n        \u251c\u2500\u2500 USB SSD  \u2192 target for OMV\n        \u2502\n        \u2514\u2500\u2500 Internal HDDs \u2192 later used for data\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">One thing is worth mentioning here.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Before doing the installation, I had expected that identifying or selecting the correct installation disk might be problematic.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>That turned out not to be the case.<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The OpenMediaVault\/Debian installer allowed me to select the correct target disk during installation.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I simply selected the external SSD and installed OMV onto it.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">So, at least with my TS-469L and this installation method, there was no need to physically remove all of the internal hard drives just to force the installer onto the correct disk.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Obviously, you should still check the disk size and device carefully before confirming the installation. Selecting the wrong disk at this stage can destroy the data on it.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">First Boot<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">After installation, I removed the installation USB stick and booted the QNAP from the USB SSD.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">OpenMediaVault started normally.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">From that point onwards, the TS-469L essentially behaved like a normal x86 Linux server.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The external SSD contains the operating system while the internal drive bays are completely available for storage.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Reinitializing the Hard Drives<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Because I had already backed up my approximately 3.5 TB of data, I did not need to worry about preserving the previous QNAP storage configuration.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I therefore reinitialized the disks in OpenMediaVault and created the storage layout from scratch.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Part of my storage is configured as a mirrored array.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For this I installed:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>openmediavault-md 8.1.5-1<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This adds the Linux MD software RAID functionality to OMV and allowed me to create my RAID1 mirror.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Conceptually:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nHDD 1 \u2500\u2500\u2500\u2500\u2500\u2510\n           \u251c\u2500\u2500 RAID1 \u2500\u2500\u25ba Mirror\nHDD 2 \u2500\u2500\u2500\u2500\u2500\u2518\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">With RAID1, the same data is stored on both disks. If one disk fails, the data remains available on the other.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It is important to remember, however:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>RAID is not a backup.<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">RAID protects against a disk failure. It does not protect against accidental deletion, malware, corrupted files or a file being overwritten.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This is one of the reasons why I additionally use rclone backups, which I describe later.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Restoring the Data<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Once the filesystems, RAID and shared folders were configured, I copied the approximately 3.5 TB of data back to the NAS.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">There was nothing particularly exciting about this step \u2014 it simply took some time.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">After the restore had completed and I had verified the data, the migration itself was basically finished.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The process was therefore surprisingly simple:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n1. Back up all data externally\n2. Create OMV installation USB stick\n3. Connect SSD to the QNAP\n4. Boot from installation USB\n5. Install OMV onto the SSD\n6. Boot OMV\n7. Reinitialize internal disks\n8. Configure RAID \/ filesystems \/ shares\n9. Restore the data\n10. Done\n<\/pre><\/div>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Useful OpenMediaVault Plugins<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">There are two OMV plugins that I currently find particularly useful.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">openmediavault-md 8.1.5-1<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">As mentioned above, I use this for my mirrored disks.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It provides the integration for Linux MD software RAID and allows the RAID configuration to be managed through OpenMediaVault.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For my setup, this is one of the essential plugins.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">openmediavault-filebrowser 8.0.6-1<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">I also installed:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>openmediavault-filebrowser 8.0.6-1<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The idea is useful because it provides a graphical file browser directly through the web interface.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">However, I personally find the functionality rather limited.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It is useful for quickly checking directories or doing some simple file operations, but I would not consider it a replacement for a proper file manager.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Still, having a browser-based file interface available on a headless NAS can occasionally be convenient.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Performance: Surprisingly Good for 13-Year-Old Hardware<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">The TS-469L is around 13 years old, so I did not expect miracles.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Nevertheless, I am quite satisfied with its performance.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Of course, this machine cannot compete with a modern NAS in terms of CPU performance, memory bandwidth or modern interfaces.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">But that isn&#8217;t what I need it to do.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>file storage<\/li>\n\n\n\n<li>SMB shares<\/li>\n\n\n\n<li>RAID1<\/li>\n\n\n\n<li>backups<\/li>\n\n\n\n<li>rclone<\/li>\n\n\n\n<li>general home NAS duties<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">it is still perfectly adequate.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">And that was ultimately the point of this project.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Instead of throwing away functioning hardware simply because the original software platform is getting old, OpenMediaVault allows the machine to continue operating as a modern Linux-based NAS.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Adding Weekly OneDrive Backups with rclone<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">After the basic NAS migration was finished, I wanted another layer of protection.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Several OneDrive accounts contain data that I want to keep locally on the NAS.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For this I use <strong>rclone<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The basic idea is:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nOneDrive\n   \u2502\n   \u2502 rclone sync\n   \u25bc\nLocal NAS copy\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">However, I wanted something slightly more sophisticated than simply running:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nrclone sync remote: \/some\/local\/folder\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">A normal <code>sync<\/code> has an important characteristic:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If a file disappears from OneDrive, rclone will eventually remove it from the destination as well.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">That is exactly what a synchronization tool is supposed to do, but it isn&#8217;t necessarily what I want from a backup.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">So I created a script around rclone that combines synchronization with a temporary archive.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I used <strong>ChatGPT extensively to help develop, review and refine this script<\/strong>. What started as a relatively simple rclone command eventually became a much more comprehensive backup script with safety checks, locking, logging, statistics, archive retention and separate handling for certain directories.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The following is a simplified and anonymized version of my setup.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Directory Structure<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">My real paths contain disk UUIDs and personal directory names, so I have replaced them here with generic examples.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Assume the NAS contains:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n\/storage\/raid1\/\n    Backup-OneDrive\/\n\n\/storage\/data\/\n    OneDrive\/\n    Archive\/\n\n\/var\/log\/rclone-backup\/\n\n\/etc\/rclone\/rclone.conf\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">In my setup, photographs are stored on the mirrored storage while other OneDrive data can be stored on another data disk.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For example:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nRAID1\n\u2514\u2500\u2500 Backup-OneDrive\n    \u2514\u2500\u2500 Pictures\n        \u251c\u2500\u2500 User-A\n        \u251c\u2500\u2500 User-B\n        \u2514\u2500\u2500 Photos\n\nData Disk\n\u251c\u2500\u2500 OneDrive\n\u2502   \u251c\u2500\u2500 User-A\n\u2502   \u251c\u2500\u2500 User-B\n\u2502   \u251c\u2500\u2500 User-C\n\u2502   \u2514\u2500\u2500 User-D\n\u2502\n\u2514\u2500\u2500 Archive\n<\/pre><\/div>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">rclone Configuration<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">My rclone configuration is stored centrally at:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n\/etc\/rclone\/rclone.conf\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">The real file contains the authentication information for the OneDrive remotes.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For example, after configuring rclone, you might have remotes such as:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nOneDrive-UserA:\nOneDrive-UserB:\nOneDrive-UserC:\nOneDrive-Photos:\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">These names are arbitrary. They are simply the names assigned while running:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nrclone config\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">The configuration file should obviously be protected appropriately because it contains credentials\/tokens that allow rclone to access the cloud accounts.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">The Backup Script<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">At the beginning of the script I define the important locations:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n#!\/bin\/bash\n\nset -u\n\nPATH=&quot;\/usr\/local\/sbin:\/usr\/local\/bin:\/usr\/sbin:\/usr\/bin:\/sbin:\/bin&quot;\numask 0002\n\nCONFIG=&quot;\/etc\/rclone\/rclone.conf&quot;\n\nRAID=&quot;\/storage\/raid1&quot;\nDATA=&quot;\/storage\/data&quot;\n\nARCHIVE=&quot;$DATA\/Archive&quot;\nLOGDIR=&quot;\/var\/log\/rclone-backup&quot;\nLOCKFILE=&quot;\/root\/.onedrive-backup.lock&quot;\n\nDATE=&quot;$(date +%F_%H-%M-%S)&quot;\nSTART_TIME=&quot;$(date &#039;+%d.%m.%Y %H:%M:%S&#039;)&quot;\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s look at what these settings do.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><code>set -u<\/code><\/h3>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nset -u\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">This makes Bash stop when the script attempts to use an undefined variable.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">That helps detect typos or programming mistakes instead of silently continuing with an empty value.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">PATH<\/h3>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nPATH=&quot;\/usr\/local\/sbin:\/usr\/local\/bin:\/usr\/sbin:\/usr\/bin:\/sbin:\/bin&quot;\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">Scheduled jobs do not necessarily receive the same environment as an interactive SSH session.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Defining <code>PATH<\/code> explicitly makes sure commands such as <code>rclone<\/code>, <code>find<\/code>, <code>mkdir<\/code> and <code>flock<\/code> can be located when the script runs automatically.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">umask<\/h3>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\numask 0002\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">This makes newly created files\/directories group-writable, which fits my OMV permission model.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Preventing Two Backups from Running Simultaneously<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">One of the safety features I added is a lock.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nexec 9&gt;&quot;$LOCKFILE&quot;\n\nif ! flock -n 9; then\n    echo &quot;ERROR: Another OneDrive backup is already running.&quot;\n    exit 1\nfi\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">This is important.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Imagine that the weekly backup is still running for some reason and another execution starts.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">You could then have two instances of rclone modifying the same destination and archive directories simultaneously.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>flock<\/code> prevents that.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The first script obtains the lock.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If another instance starts, it detects the existing lock and immediately exits.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Preparing Directories<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">The script contains a small helper function:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nprepare_dir() {\n    DIR=&quot;$1&quot;\n\n    mkdir -p &quot;$DIR&quot;\n    chgrp users &quot;$DIR&quot; 2&gt;\/dev\/null || true\n    chmod g+rws &quot;$DIR&quot; 2&gt;\/dev\/null || true\n}\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">This does three things.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">First:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nmkdir -p\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">creates the directory if it does not already exist.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Second:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nchgrp users\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">assigns the directory to the <code>users<\/code> group.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Finally:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nchmod g+rws\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">makes the directory group-writable and sets the SGID bit.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The SGID bit is useful for shared storage because new files and directories inherit the parent directory&#8217;s group.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">A Safety Whitelist for Backup Targets<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">This is one of the parts of the script that I particularly like.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Before rclone is allowed to synchronize anything, the destination is checked against a list of explicitly permitted paths.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A simplified version looks like this:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\ncheck_safe_target() {\n    TARGET=&quot;$1&quot;\n\n    case &quot;$TARGET&quot; in\n        &quot;$RAID\/Backup-OneDrive\/Pictures\/User-A&quot;|\\\n        &quot;$RAID\/Backup-OneDrive\/Pictures\/User-B&quot;|\\\n        &quot;$DATA\/OneDrive\/User-A&quot;|\\\n        &quot;$DATA\/OneDrive\/User-B&quot;|\\\n        &quot;$DATA\/OneDrive\/User-C&quot;)\n            return 0\n            ;;\n\n        *)\n            echo &quot;SAFETY ABORT: Unsafe destination:&quot;\n            echo &quot;$TARGET&quot;\n            exit 1\n            ;;\n    esac\n}\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">Why bother doing this?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Because <code>rclone sync<\/code> is destructive by design.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The destination is changed to match the source.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A typo such as accidentally specifying:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n\/storage\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">instead of:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n\/storage\/data\/OneDrive\/User-A\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">could therefore have very unpleasant consequences.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The whitelist means the script only accepts destinations that I have explicitly defined.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Anything else causes an immediate abort.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">The Actual rclone Command<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">The central part of the script eventually executes something equivalent to:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nrclone sync \\\n    &quot;$SOURCE&quot; \\\n    &quot;$TARGET&quot; \\\n    --config &quot;$CONFIG&quot; \\\n    --backup-dir &quot;$BACKUP&quot; \\\n    --create-empty-src-dirs \\\n    --delete-after \\\n    --max-delete 1000 \\\n    --transfers 2 \\\n    --checkers 4 \\\n    --log-level INFO\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">This deserves a closer look.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><code>rclone sync<\/code><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Unlike <code>copy<\/code>, <code>sync<\/code> tries to make the destination match the source.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If OneDrive contains:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nA\nB\nC\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">and the NAS contains:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nA\nB\nC\nD\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">then a normal sync would remove <code>D<\/code> from the NAS.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">That is why the next option is so important.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\"><code>--backup-dir<\/code><\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Every run gets its own timestamp:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nDATE=&quot;$(date +%F_%H-%M-%S)&quot;\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">For example:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n2026-09-06_03-00-00\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">The archive can therefore look like:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nArchive\n\u2514\u2500\u2500 OneDrive\n    \u2514\u2500\u2500 User-A\n        \u251c\u2500\u2500 2026-09-06_03-00-00\n        \u251c\u2500\u2500 2026-09-13_03-00-00\n        \u2514\u2500\u2500 2026-09-20_03-00-00\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">The rclone option:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n--backup-dir &quot;$BACKUP&quot;\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">means that files which would otherwise be overwritten or deleted at the destination are moved into the backup directory.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This changes the character of the synchronization significantly.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Instead of:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nCloud deletion\n      \u2502\n      \u25bc\nLocal file deleted\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">I effectively get:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nCloud deletion\n      \u2502\n      \u25bc\nRemoved from current mirror\n      \u2502\n      \u25bc\nMoved into timestamped archive\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">This gives me an additional recovery window.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\"><code>--delete-after<\/code><\/h1>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n--delete-after\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">tells rclone to process deletions after transfers have completed.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I prefer this behaviour because the new\/changed files are transferred before obsolete destination files are dealt with.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\"><code>--max-delete 1000<\/code><\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Another safety mechanism is:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n--max-delete 1000\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">This limits the number of deletions rclone is allowed to perform during one run.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The reason is simple.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Suppose something goes seriously wrong with a OneDrive remote or I accidentally point the script at an empty source.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Without protection, a synchronization could interpret this as:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">The cloud is empty, therefore the local mirror must also become empty.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">A deletion limit provides an additional barrier against a catastrophic mass deletion.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It isn&#8217;t a replacement for backups, but it is a useful safety mechanism.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\"><code>--transfers 2<\/code> and <code>--checkers 4<\/code><\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">I deliberately keep the concurrency relatively conservative:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n--transfers 2\n--checkers 4\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">Only two file transfers are performed simultaneously, while four checker operations can run concurrently.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Remember: this is a 13-year-old NAS.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">There is little point in aggressively increasing concurrency if the CPU, disks or network connection cannot benefit from it.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For my hardware, this conservative configuration works well.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Separating Pictures from the Main OneDrive Backup<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">For some accounts, I do not want the picture directories stored on the same disk as the rest of the OneDrive data.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Therefore, the main sync excludes them:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n--exclude &quot;\/Pictures\/**&quot; \\\n--exclude &quot;\/Pictures\/&quot;\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">The picture directory is then synchronized separately to the RAID1 storage:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nOneDrive User\n\u2502\n\u251c\u2500\u2500 Documents \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u25ba Data Disk\n\u251c\u2500\u2500 Other files \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u25ba Data Disk\n\u2502\n\u2514\u2500\u2500 Pictures \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u25ba RAID1\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">This lets me decide which type of data should live on which physical storage.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Checking Whether Optional Cloud Folders Exist<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Some accounts may contain particular picture directories while others may not.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Instead of treating a missing directory as an error, the script first checks whether it exists:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nrclone lsf &quot;$REMOTE&quot; \\\n    --config &quot;$CONFIG&quot; \\\n    --dirs-only \\\n    --max-depth 1\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">If the folder exists, the synchronization is performed.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If it doesn&#8217;t, the script simply records something similar to:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nCloud folder not present - skipped\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">This makes the same script usable even if the directory structure differs slightly between accounts.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Logging<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Every backup job has a persistent log file:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n\/var\/log\/rclone-backup\/\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">For example:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nuser-a-onedrive.log\nuser-a-pictures.log\nuser-b-onedrive.log\nphotos.log\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">In addition, each execution creates temporary working files.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The script counts operations such as:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nCopied\nDeleted\nMoved\/Renamed\nErrors\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">and generates a summary.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A typical result might look conceptually like:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nUser A OneDrive:       23 copied | 2 deleted | 1 moved | 0 errors | OK\nUser A Pictures:      142 copied | 0 deleted | 0 moved | 0 errors | OK\nUser B OneDrive:        4 copied | 1 deleted | 0 moved | 0 errors | OK\nPhotos:                18 copied | 0 deleted | 0 moved | 0 errors | OK\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">This makes it much easier to see what actually happened during a backup without manually reading thousands of rclone log lines.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Automatic Cleanup After 90 Days<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Keeping every historical version forever would eventually fill the disk.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Therefore, my script retains the timestamped archive directories for approximately <strong>90 days<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The principle is:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nfind &quot;$ARCHIVE\/...&quot; \\\n    -type d \\\n    -mtime +90 \\\n    -exec rm -rf -- {} +\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">Afterwards, empty archive directories are removed as well.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Conceptually:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nCurrent local mirror\n       \u2502\n       \u251c\u2500\u2500 current files\n       \u2502\n       \u2514\u2500\u2500 deleted\/changed files\n                  \u2502\n                  \u25bc\n             Archive\n                  \u2502\n             kept 90 days\n                  \u2502\n                  \u25bc\n             automatically\n                removed\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">This is a compromise between having version history and not allowing the archive to grow indefinitely.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Temporary Working Directory<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">For each execution, the script creates a temporary directory:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nWORKDIR=&quot;$(mktemp -d \/tmp\/onedrive-backup.XXXXXX)&quot;\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">The temporary summary and detailed report are generated there.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A trap makes sure it is removed when the script exits:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\ntrap &#039;rm -rf &quot;$WORKDIR&quot;&#039; EXIT\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">So temporary reporting files do not accumulate over time.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Exit Codes and Monitoring<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">At the end, the script checks whether any backup job failed.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Conceptually:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nif &#x5B; &quot;$FAILURES&quot; -eq 0 ]; then\n    exit 0\nelse\n    exit 1\nfi\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">This is particularly useful when the script is executed as an OpenMediaVault scheduled job.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>0<\/code> means everything completed successfully.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>1<\/code> means at least one backup failed.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The script also writes its summary and detailed rclone output to standard output, allowing OMV&#8217;s scheduled-job reporting to capture the complete backup report.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Scheduling the Backup in OpenMediaVault<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">I run the complete synchronization <strong>once a week<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This can conveniently be configured through OpenMediaVault&#8217;s scheduled jobs.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The important part is that the scheduled job executes the script rather than containing all the rclone commands itself.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For example:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n\/path\/to\/onedrive-backup.sh\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">This has several advantages.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The OMV scheduler only needs to know when the backup should run.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">All of the actual logic remains inside the script:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nOMV Scheduled Job\n        \u2502\n        \u25bc\nBackup Script\n        \u2502\n        \u251c\u2500\u2500 Lock\n        \u251c\u2500\u2500 Check destinations\n        \u251c\u2500\u2500 Check cloud folders\n        \u251c\u2500\u2500 Run rclone jobs\n        \u251c\u2500\u2500 Archive changes\n        \u251c\u2500\u2500 Write logs\n        \u251c\u2500\u2500 Generate statistics\n        \u251c\u2500\u2500 Clean archive\n        \u2514\u2500\u2500 Return status\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">That makes the setup much easier to maintain.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Is This a Real Backup?<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">There is an important distinction here.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>rclone sync<\/code> itself is primarily a <strong>synchronization mechanism<\/strong>, not a traditional versioned backup system.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">However, combining it with:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n--backup-dir\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">and timestamped archive directories means deleted or replaced destination files are retained for a period of time.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In my case, that period is 90 days.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">So my setup provides:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nOneDrive\n   \u2502\n   \u2502 synchronization\n   \u25bc\nCurrent local copy\n   \u2502\n   \u2502 changed\/deleted files\n   \u25bc\n90-day archive\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">For my use case, this provides a useful additional local recovery layer.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Still, particularly important data should always exist independently in multiple locations.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Final Result<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">After completing the migration, my old QNAP now runs a current OpenMediaVault installation instead of relying on its original NAS operating system.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The final setup provides:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"> <code>                   \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n                    \u2502   OneDrive    \u2502\n                    \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n                            \u2502\n                         rclone\n                            \u2502\n              \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n              \u25bc                           \u25bc\n        Normal Data                   Pictures\n              \u2502                           \u2502\n              \u25bc                           \u25bc\n         Data Disk                     RAID1\n              \u2502\n              \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n                           \u2502\n                  changed\/deleted files\n                           \u2502\n                           \u25bc\n                    90-Day Archive<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">For hardware that is around <strong>13 years old<\/strong>, I am genuinely satisfied with the result.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The installation itself was much easier than I originally expected. The installer allowed me to select my external SSD as the operating-system disk, the internal drives could then be completely reinitialized, and after restoring the roughly 3.5 TB of data the NAS was ready to use again.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The additional rclone automation gives me something I did not simply want to leave to manual processes: regular local copies of my cloud data, a recovery period for deleted files, logs, safety checks and clear reporting.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I also found it interesting how the backup script evolved. I used <strong>ChatGPT to help create and iteratively improve it<\/strong>, especially when adding the safety mechanisms, logging, archive handling and reporting.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Would I replace a modern high-performance NAS with a TS-469L today?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Probably not.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">But if you already own one?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">There is surprisingly little reason to throw away perfectly functional x86 hardware just because it is old.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For file storage, RAID, SMB and automated backups, my TS-469L still does exactly what I need it to do.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">And OpenMediaVault gave it a second life.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>My QNAP TS-469L is now around 13 years old. From a hardware perspective, that sounds ancient, especially when compared with modern NAS systems. Nevertheless, the device is still perfectly usable for what I need: network storage, RAID, SMB shares and backups. Instead of replacing perfectly functional hardware, I decided to give the TS-469L a second<\/p><\/div>\n<div class=\"blog-btn\"><a href=\"https:\/\/gentz.cloud\/?p=1053\" class=\"home-blog-btn\">Read More<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-1053","post","type-post","status-publish","format-standard","hentry","category-tips-ricks"],"_links":{"self":[{"href":"https:\/\/gentz.cloud\/index.php?rest_route=\/wp\/v2\/posts\/1053","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/gentz.cloud\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/gentz.cloud\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/gentz.cloud\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/gentz.cloud\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1053"}],"version-history":[{"count":2,"href":"https:\/\/gentz.cloud\/index.php?rest_route=\/wp\/v2\/posts\/1053\/revisions"}],"predecessor-version":[{"id":1056,"href":"https:\/\/gentz.cloud\/index.php?rest_route=\/wp\/v2\/posts\/1053\/revisions\/1056"}],"wp:attachment":[{"href":"https:\/\/gentz.cloud\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1053"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/gentz.cloud\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1053"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/gentz.cloud\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1053"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}