January 26Jan 26 par2cron for UNRAIDhttps://github.com/desertwitch/par2cronhttps://github.com/desertwitch/par2cron-unRAIDPAR2 Integrity & Self-Repair EngineSelective automated protection for directory treespar2cron is a tool that wraps par2cmdline (a parity-based file recovery tool) to achieve automated periodic integrity creation, verification and repair within any given directory tree. It is designed for use with non-changing WORM-type of files, perfect for adding a degree of protection to media libraries or backups. The driving idea is that you do not need to invest in a filesystem (like ZFS) that protects all your data, at the disadvantage of additional complexities, when you really only care that important subsets of your data remain protected.The array is scanned for marker files, and a PAR2 set created for every directory containing such a _par2cron file. For verification, the program loads the PAR2 sets and verifies that the data which they are protecting is healthy, otherwise flagging the PAR2 set for repair. Once repair runs, corrupted or missing files are recovered. Many command-line tunables, as well as configuration directives, are offered for more granular adjustment of how to create, when to verify and in what situation to repair.It being set up, you can simply protect any valuable folder by just placing a _par2cron file in it; the tool will create a PAR2 set and pick it up into the periodic verification and repair cycle - now protected from corruption/bitrot (with e.g. 15% redundancy, consuming less disk space than a full-redundancy solution).Installable via Community Applications
January 30Jan 30 this seems nice, i do wish there was the ability to recursively go into directories though, trying it out.
January 30Jan 30 Author 29 minutes ago, oliver said:this seems nice, i do wish there was the ability to recursively go into directories though, trying it out.Recursive creation will be possible within the next 1-2 updates, I'm currently working on this (as well as some other features).
January 30Jan 30 I am not ready to migrate to ZFS and likely never will so the subject is very important to me and I just stumbled upon this plugin. So far I have been using File Integrity plugin, but I am no expert on the mechanics involved.Genuine question, would you recommend your solution and if so why? Is it faster or more resilient or other?
January 31Jan 31 Author 13 hours ago, rug said:I am not ready to migrate to ZFS and likely never will so the subject is very important to me and I just stumbled upon this plugin. So far I have been using File Integrity plugin, but I am no expert on the mechanics involved.Genuine question, would you recommend your solution and if so why? Is it faster or more resilient or other?I'm not an expert on the "File Integrity" plugin, but from what I know it only does checksumming.This means it calculates checksums for all your data, and you can see if something changed (but not repair).On the other hand, par2cron doesn't protect all your data, but lets you create protection sets for important data.Those sets it verifies the integrity, so you can see if something changed, and lets you repair in case of corruption (unless severe).
February 1Feb 1 23 hours ago, Rysz said:I'm not an expert on the "File Integrity" plugin, but from what I know it only does checksumming.This means it calculates checksums for all your data, and you can see if something changed (but not repair).On the other hand, par2cron doesn't protect all your data, but lets you create protection sets for important data.Those sets it verifies the integrity, so you can see if something changed, and lets you repair in case of corruption (unless severe).Thanks for the answer! So this means with par2cron we could repair on the server whereas with fileintegrity plugin we would have to restore from backup.From my point of view your solution would help with files with regular updates and give them special most current protection on the host. The question comes down to how secure you want to have it and how often we believe to expect issues and how much effort restore would be. Edited February 1Feb 1 by perpetuum.mobile
February 3Feb 3 Author On 2/1/2026 at 11:22 AM, perpetuum.mobile said:Thanks for the answer! So this means with par2cron we could repair on the server whereas with fileintegrity plugin we would have to restore from backup.From my point of view your solution would help with files with regular updates and give them special most current protection on the host. The question comes down to how secure you want to have it and how often we believe to expect issues and how much effort restore would be.The idea is quite the opposite actually, it's for files which do not change often and just sit there.Think your family photos, recordings of your super rare favourite TV series, anything irreplaceable.These are the files where you won't notice corruption until you open them again in one or two years.The program checks these files continuously so you can rest at ease that they are still in tip top shape.Files which you regularly update or interact with, you'll likely notice problems, and for those ZFS is better.On 1/30/2026 at 9:39 PM, oliver said:this seems nice, i do wish there was the ability to recursively go into directories though, trying it out.Recursive mode is added in the latest update, the quick guide and documentation have been updated with how to use it.
April 4Apr 4 This is bloody brilliant! I was considering switching to ZFS, but with this there's really no need.I'm still testing, but it looks very promising. Could arguably be one of the best plugins for UnRAID ever in my mind ;) Just out of interest though, why is the default redundancy set to 15%? If this is primarily to address bitrot, the par2 default of 5% is more than adequate, isn't it?
April 4Apr 4 Author 4 hours ago, Karools said:This is bloody brilliant! I was considering switching to ZFS, but with this there's really no need.I'm still testing, but it looks very promising. Could arguably be one of the best plugins for UnRAID ever in my mind ;)Just out of interest though, why is the default redundancy set to 15%? If this is primarily to address bitrot, the par2 default of 5% is more than adequate, isn't it?No special reason, it's just what I use personally and I wanted to show how to set something different than the default. 😉But you're right, of course, 5% should be just fine for the random bit flip that people will want to guard against ("bit rot")...
April 6Apr 6 The only issue I have so far is that the glob seems rather limited. It only allows for a single extension. There's no brace expansion support, unless I'm doing it wrong.I'm using this in a media library setting, I'm using a recursive marker per movie/season - that's the idea for now anyway. The problem is that these folders tend to contain lots of images and nfo files that can change quite frequently.Ideally I would like to add a recursive marker with something like the following to the root of each movie - glob: "**/*.{mkv,avi,mp4,idx,sub,srt,ass,ssa}
April 6Apr 6 Author 16 minutes ago, Karools said:The only issue I have so far is that the glob seems rather limited. It only allows for a single extension. There's no brace expansion support, unless I'm doing it wrong.I'm using this in a media library setting, I'm using a recursive marker per movie/season - that's the idea for now anyway. The problem is that these folders tend to contain lots of images and nfo files that can change quite frequently.Ideally I would like to add a recursive marker with something like the following to the root of each movie - glob: "**/*.{mkv,avi,mp4,idx,sub,srt,ass,ssa}Thanks for the valid feedback, glob support is indeed relatively (too) limited.I have good experiences with the doublestar library, would these patterns help you achieve what you want?https://github.com/bmatcuk/doublestar#patternsIf so, I can probably relatively seamlessly switch the current glob implementation to this more featureful one.
April 6Apr 6 That should work a treat yes, it looks exactly like what we're after.I'd very much appreciate if you could look into this when you have a moment, I'm fairly certain other users would also find it very useful.
April 7Apr 7 Author 7 hours ago, Karools said:That should work a treat yes, it looks exactly like what we're after.I'd very much appreciate if you could look into this when you have a moment, I'm fairly certain other users would also find it very useful.The latest update now contains the changed implementation.You will need to use either folder (one PAR2 set, see below) or file (one PAR2 set per file) creation modes for any glob possibly spanning multiple folders.The reason is that recursive mode triggers par2cmdline's "dumb" recursion (which just includes everything in subfolders), and we don't want double-recursion.See more about this in the documentation, but the program will error anyway if you made a mistake: https://github.com/desertwitch/par2cron#creation-glob-patternsGlob pattern **/*.{mkv,avi,mp4,idx,sub,srt,ass,ssa} (in folder creation mode) will now create for this example folder:Run All Day (720p) ├── Run All Day.mkv ├── Run All Day.orig.nfo ├── _par2cron └── sub ├── Run All Day.en.sub └── Run All Day.idxOne PAR2 set called Run All Day (720p).par2 (in the marker-containing directory) protecting: { "name": "Run All Day.mkv", "size": 5863690768, "mode": 511, "is_dir": false, "mod_time": "2015-06-02T15:13:02.734571313+02:00" }, { "name": "sub/Run All Day.en.sub", "size": 9330688, "mode": 511, "is_dir": false, "mod_time": "2015-06-02T13:57:01.121948951+02:00" }, { "name": "sub/Run All Day.idx", "size": 59657, "mode": 511, "is_dir": false, "mod_time": "2015-06-02T13:57:01.503980322+02:00" }I believe this is what you want to achieve with your pattern, but let me know. 😉
April 7Apr 7 Oh my goodness you're quick 😶The recursion thing makes sense, recursion is basically assumed based on the glob.Right, bear in mind it's late evening and I've already got a few glasses of wine in me, but I'm fairly certain I haven't made any mistakes.I have one new issue now that we didn't have before.With Plex you can give media an anchor or a tag to help prevent things from being mismatched; or a tag to distinguish between editions, like Director's Cut, Extended etc., in the form of curly brackets - https://support.plex.tv/articles/naming-and-organizing-your-movie-media-files/It seems happy to process a filename with curly braces, even subfolders, but not when it's the folder containing the _par2cron marker.└── Movies └── Big Buck Bunny (2008) {imdb-tt1254207} ├── Big Buck Bunny (2008) {imdb-tt1254207} - 1080p 30fps normal.mp4 ├── Big Buck Bunny (2008) {imdb-tt1254207} - 2160p 30fps normal.idx ├── Big Buck Bunny (2008) {imdb-tt1254207} - 2160p 30fps normal.mp4 ├── Big Buck Bunny (2008) {imdb-tt1254207} - 2160p 30fps normal.srt ├── Big Buck Bunny (2008) {imdb-tt1254207} - 2160p 30fps normal.sub ├── Featurettes │ ├── big_buck_bunny_720p_surround.avi │ ├── sub_folder │ │ └── BigBuckBunny_320x180.mp4 │ └── sub_folder with braces {hello} │ └── big_buck_bunny_720p_surround.avi ├── landscape.jpg ├── logo.png ├── _par2cron └── poster.jpgThat results in an error where it claims it can't find anything:par2cron create -c ~/par2cron.yaml . 22:24:03 INF Scanning filesystem for jobs... op=create path=/home/acme/test/media 22:24:03 INF Starting to process 1 jobs... op=create path=/home/acme/test/media maxDuration=0s 22:24:03 INF Job started op=create job="/home/acme/test/media/Movies/Big Buck Bunny (2008) {imdb-tt1254207}/_par2cron" job_position=1/1 args=[] glob=**/*.{mkv,avi,mp4,idx,sub,srt,ass,ssa} mode=folder hidden=false verify=false 22:24:03 ERR No files to protect in folder (will check again next run) op=create path="/home/acme/test/media/Movies/Big Buck Bunny (2008) {imdb-tt1254207}" job="/home/acme/test/media/Movies/Big Buck Bunny (2008) {imdb-tt1254207}/_par2cron" job_position=1/1 args=[] glob=**/*.{mkv,avi,mp4,idx,sub,srt,ass,ssa} mode=folder hidden=false verify=false 22:24:03 ERR Job failure (will retry next run) op=create job="/home/acme/test/media/Movies/Big Buck Bunny (2008) {imdb-tt1254207}/_par2cron" job_position=1/1 args=[] glob=**/*.{mkv,avi,mp4,idx,sub,srt,ass,ssa} mode=folder hidden=false verify=false error="failed to find to-protect elements: no files to protect" 22:24:03 ERR Operation completed with errors (1/1 jobs processed) op=create error="partial failure: failed to find to-protect elements: no files to protect" successCount=0 skipCount=0 errorCount=1 processedCount=1 selectedCount=1 Error: create: partial failure: failed to find to-protect elements: no files to protectBut if you remove the braces from the main folder, it happy does its thing, including subfolders with curly braces:par2cron create -c ~/par2cron.yaml . 22:24:21 INF Scanning filesystem for jobs... op=create path=/home/acme/test/media 22:24:21 INF Starting to process 1 jobs... op=create path=/home/acme/test/media maxDuration=0s 22:24:21 INF Job started op=create job="/home/acme/test/media/Movies/Big Buck Bunny (2008)/_par2cron" job_position=1/1 args=[] glob=**/*.{mkv,avi,mp4,idx,sub,srt,ass,ssa} mode=folder hidden=false verify=false Block size: 821036 Source file count: 8 Source block count: 2000 Recovery block count: 100 Recovery file count: 7 Opening: Big Buck Bunny (2008) {imdb-tt1254207} - 1080p 30fps normal.mp4 Opening: Big Buck Bunny (2008) {imdb-tt1254207} - 2160p 30fps normal.idx Opening: Big Buck Bunny (2008) {imdb-tt1254207} - 2160p 30fps normal.mp4 Opening: Big Buck Bunny (2008) {imdb-tt1254207} - 2160p 30fps normal.srt Opening: Big Buck Bunny (2008) {imdb-tt1254207} - 2160p 30fps normal.sub Opening: Featurettes/big_buck_bunny_720p_surround.avi Opening: Featurettes/sub_folder with braces {hello}/big_buck_bunny_720p_surround.avi Opening: Featurettes/sub_folder/BigBuckBunny_320x180.mp4 Computing Reed Solomon matrix. Constructing: done. Wrote 82103600 bytes to disk Writing recovery packets Writing verification packets Done 22:25:11 INF Succeeded to create PAR2 op=create path="/home/acme/test/media/Movies/Big Buck Bunny (2008)/Big Buck Bunny (2008).par2" job="/home/acme/test/media/Movies/Big Buck Bunny (2008)/_par2cron" job_position=1/1 args=[] glob=**/*.{mkv,avi,mp4,idx,sub,srt,ass,ssa} mode=folder hidden=false verify=false 22:25:11 INF Job completed with success op=create job="/home/acme/test/media/Movies/Big Buck Bunny (2008)/_par2cron" job_position=1/1 args=[] glob=**/*.{mkv,avi,mp4,idx,sub,srt,ass,ssa} mode=folder hidden=false verify=false 22:25:11 INF Operation completed (1/1 jobs processed) op=create successCount=1 skipCount=0 errorCount=0 processedCount=1 selectedCount=1Apart from that, it's nearly perfect 🙃
April 7Apr 7 Author Thanks for the quick response and detailed report.The problem should be fixed with the most recent update of the plugin (2026.04.07d/v0.2.2).I've got a few more improvements planned, namely I want to add another creation mode "nested".In nested mode a self-contained PAR2 set shall be created for any matched (sub-)folder's contents.Placing one marker in a top-level "Movies" would create "Movies/A/A.par2", "Movies/B/B.par2", ... sets.Marker files could be set to persistent, specifically for "nested", with PAR2 sets for new folders created from one top-level marker file.The scheduled creation would just pick up that marker file repeatedly, skip folders with existing PAR2 sets, and create sets for those without any.I figured this could be helpful for growing media libraries with a continuous intake of new content, without requiring marker file recreation.Updating a PAR2 set could then be as easy as just deleting the old PAR2 set, without ever needing to recreate a marker file... that's how I pictured it.Anyway this will most likely be part of a more extensive effort, especially on the documentation side, so this'll probably take me a longer while. 😉
April 7Apr 7 any way to limit which shares it searches for _par2cron? takes way too long to search through 15 drives that it shouldnt be searching.
April 7Apr 7 Firstly, the curly bracket issue is indeed fixed now with v0.2.2 - thank you very much. It's working perfectly.And the nested idea sounds great, it would certainly be of use to people who use *arrs for automation, at least for Movies. For current/ongoing Series not as much, for obvious reasons.. but there are ways to work around that 🤔By the way, what @paulio23 mentioned is a bit of a problem for me too. I added markers to 4 movies for testing. After manually kicking off the Create task, it took over an hour for it to get going.
April 8Apr 8 Author Thanks for the responses, that definitely feels like it's too long. I'll look into it, how many disks and TBs do you have? Ideally if you know/could check how many files your /mnt/user contains, I could tell if it's a bug or really that slow because of the amount of files. Also, was the indexing always this slow or it got massively worse with this week's update?
April 8Apr 8 I suspect it's the sheer number of small files and folders, but they all sit outside of the of folder I'm actually interested in running this on. If there is a way of limiting the plugin to specific paths, that would be great.This setup consists of 24 x 12TB SAS drives + 6 SSDs for cache. Overall speed is quite good./mnt/user:time find /mnt/user -type f | wc -l 6570518 real 6m50.679s user 0m13.044s sys 0m36.744s time du -hs /mnt/user 142T /mnt/user real 17m1.591s user 0m17.075s sys 1m53.070s/mnt/user/media:time find /mnt/user/media -type f | wc -l 725466 real 0m22.400s user 0m0.862s sys 0m1.901s time du -hs /mnt/user/media 132T /mnt/user/media real 1m42.175s user 0m1.278s sys 0m9.025sAs you can see the difference in duration there is rather significant.I honestly don't know how slow it was prior to the glob updates. I don't recall it being particularly slow or snappy. Most of my tests were done on a different machine. Edited April 8Apr 8 by Karools
April 8Apr 8 Author Was this during a parity check or something that could impact I/O?Because I cannot replicate this, a similar setup with 4 million files completes in less than 10 minutes here.I'm optimising hot paths some more, but I cannot figure out what would cause a one hour filesystem scan.Unless it's something in your environment that's severely bottle-necking I/O, but your find is much faster...Anything unusual come to mind about your environment or setup as a whole?I'll look into allowing choice of path or shares... but still the one hour seems way out of proportion here.
April 8Apr 8 Nope, no parity check or anything too IO intensive.I had to replace a drive over the weekend, which is actually what prompted me to go looking into the integrity plugin - and ultimately lead me to your fine par2 implementation.I should have tried this earlier..Running par2cron against the media share, it completes the scan in 2m24s:par2cron create -c /boot/config/plugins/dwpar2cron/config.yaml /mnt/user/media 19:04:42 INF Scanning filesystem for jobs... op=create path=/mnt/user/media 19:07:06 INF Starting to process 28 jobs... op=create path=/mnt/user/media maxDuration=0sAgainst the whole array, we jump up to 1h7m51s (very similar to yesterday in fact):par2cron create -c /boot/config/plugins/dwpar2cron/config.yaml /mnt/user 19:07:36 INF Scanning filesystem for jobs... op=create path=/mnt/user 20:15:27 INF Starting to process 28 jobs... op=create path=/mnt/user maxDuration=0sI don't know, but there's definitely something about the other shares that throws the poor thing off. Maybe I'll run par2cron against each of them and try to narrow it down.If it's not the amount of files, could it be the amount of folders?During this scan the drives were practically idle btw. There's one guy streaming something from Plex (another machine) via NFS.-- Update --Though not entirely to blame, it looks like the system share is the main culprit. This is the one used for container images and things.This folder exists only on SSD for me, it's 160GB in total, consisting of 3216305 files and 506124 folders.And it takes par2cron 34m39s to process:par2cron create -c /boot/config/plugins/dwpar2cron/config.yaml /mnt/user/system 20:57:56 INF Scanning filesystem for jobs... op=create path=/mnt/user/system 21:32:35 INF Nothing to do (will check again next run) op=create path=/mnt/user/system Edited April 8Apr 8 by Karools
April 8Apr 8 Author Many thanks for bearing with me here, I think I've identified the core issue.The problem was that the ignore-file-checker added two stat syscalls per directory, that can quickly scale up.When initially designing this, I didn't really tune this path for performance thinking most people will run it at night...But I've now refactored that and reduced it to the one necessary syscall and mostly zero allocations in the hot path.I'd have a pre-release version ready for testing with the fixes implemented, reducing scanning time by ~50+%.I'd be very grateful if you could test if this version significantly reduces the scanning time against /mnt/user.Just take care that you're executing this version and not the old (installed) one...If it still feels like it's too long, you can run it with par2cron create /mnt/user --pprof perf.pb.gzThat will create a performance profile (just of what the application is doing internally, no paths or sensible data)If you'd upload (or DM) that for me I can take a look at what is causing the wait and if it's I/O or a congested application path.Again many thanks for your feedback, it's much appreciated. 😉par2cron_0.2.2-SNAPSHOT-d7812bf_linux_amd64.tar.gz
April 9Apr 9 Oh no, not at all. This is a critical part of UnRAID for me now, optimisation is important ;DIt's interesting, doing the media share by iteself now the time has doubled, but doing the whole array, it's been cut in half (more or less):./par2cron -v par2cron version 0.2.2-SNAPSHOT-d7812bf ./par2cron create -c /boot/config/plugins/dwpar2cron/config.yaml /mnt/user/media 13:21:18 INF Scanning filesystem for jobs... op=create path=/mnt/user/media walker=os 13:27:23 INF Starting to process 28 jobs... op=create path=/mnt/user/media maxDuration=0s ./par2cron create -c /boot/config/plugins/dwpar2cron/config.yaml /mnt/user 13:28:11 INF Scanning filesystem for jobs... op=create path=/mnt/user walker=os 14:03:25 INF Starting to process 28 jobs... op=create path=/mnt/user maxDuration=0s ./par2cron create -c /boot/config/plugins/dwpar2cron/config.yaml /mnt/user --pprof perf.pb.gz 14:31:58 INF Scanning filesystem for jobs... op=create path=/mnt/user walker=os 15:07:53 INF Starting to process 28 jobs... op=create path=/mnt/user maxDuration=0sIt's certainly more manageable; and I guess under normal circumstances it won't matter if it takes 30 min longer to iterate through the entire array.But still, is it really necessary to do so.perf.pb.gz
April 9Apr 9 Author Many thanks for the performance profile, it confirms there's no application level bottlenecks anymore.I/O wait aside, the program would be done in 1.5 minutes for the whole array, but there's significant waiting on I/O.Meaning it's the program is spending all that time waiting on the disks to seek and return the requested information.The media share being slower now is likely due to caching effects, maybe more information was cached at the previous run. File: par2cron Type: cpu Time: 2026-04-09 04:31:58 CEST Duration: 2157.09s, Total samples = 98.30s ( 4.56%) Showing nodes accounting for 98.30s, 100% of 98.30s total ----------------------------------------------------------+------------- flat flat% sum% cum cum% calls calls% + context ----------------------------------------------------------+------------- 77.84s 100% | syscall.RawSyscall6 syscall/syscall_linux.go:65 0.01s 0.013% | internal/runtime/syscall/linux.EpollWait internal/runtime/syscall/linux/syscall_linux.go:32 77.85s 79.20% 79.20% 77.85s 79.20% | internal/runtime/syscall/linux.Syscall6 internal/runtime/syscall/linux/asm_linux_amd64.s:36This is unfortunately the hardware limitation here, so there's nothing more we can optimise except for limiting the scope itself.I'll get this new version released today together with changes to allow selecting only a specific share to run par2cron against.I want to allow selecting multiple shares to run it against, but that's a larger task, so for now it'll only be whole array or one specific share.Thanks again for all your feedback, without it I wouldn't have been able to make these improvements. 😉
April 10Apr 10 Author @paulio23 @Karools The new version is now released with the performance fixes and the ability to select one or multiple shares to use par2cron on.Many thanks for your valuable feedback and testing. 💛
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.