Release: 2.3.0.107 (Canary) 2026-07-13

2.3.0.107_canary_2026-07-13

This release is a canary release intended to be used for testing.

If no major faults are found, this release is intended to be the basis of the next beta release.

Breaking change: Locked-down data folder permissions

This release hardens security around the data folder and is a breaking change for some setups.

Duplicati now requires that the data folder has the exact expected permissions, or it will refuse to use it. Previously, Duplicati would silently lock down the folder if it was not already locked. The previous insecure-permissions.txt opt-out file is no longer supported.

The only way to opt out of the permission check is to pass --allow-insecure-datafolder or set the environment variable DUPLICATI__ALLOW_INSECURE_DATAFOLDER=true. This setting can be placed in preload.json, but only if that file is in a trusted location (the directory with the binaries, or the path pointed to by DUPLICATI_PRELOAD_SETTINGS). The previous trusted paths /usr/local/share/Duplicati/preload.json and C:\ProgramData\Duplicati\preload.json are no longer supported as they cannot be guaranteed to be locked down.

A preload.json inside the data folder is still supported, provided the folder passes the permission check.

The ConfigureTool has a new secure-datafolder command that can be used to force the correct permissions on the data folder.

For most users this should not cause any problems, as Duplicati has been locking down the folder already, but if you rely on lax folder permissions the setup needs to change. Some Docker setups may not be able to set the permissions and will need to apply DUPLICATI__ALLOW_INSECURE_DATAFOLDER=true in the image to run without the protections.

Feedback requested

We are grateful for any feedback on changes or hints for how we can lessen the impact before the next stable release.

Live reporting module

This release adds a new live-reporting module that sends the current progress of backups to a user-specified URL. The intention is that this can be used for dashboards that want to show the current progress for backups. By default, the module is not configured and has no impact.

The module supports multiple activity targets and includes metadata in the activity report, as well as a server-provided activity URL.

PAR2 parity / error-correction for remote volumes

This release adds a pluggable parity module that produces error-correction data for remote data volumes, so they can be repaired after bit-rot or corruption on the backend, thanks @JamBalaya56562.

A pluggable IParity module system is introduced, mirroring the existing compression/encryption modules. The default Par2Parity implementation shells out to the par2 (par2cmdline) program. If the par2 command is installed this will transparently handle creating and using parity files to guard against flipped bits.

Improved TLS certificate validation

The TLS certificate validation has been improved, and Duplicati now falls back to using the OS-default certificate validator. This should resolve issues with custom certificate chains and improve compatibility with various TLS setups.

Detailed list of changes

  • Locked down data folder permissions and require exact permissions, with opt-out via --allow-insecure-datafolder
  • Added secure-datafolder command to the ConfigureTool
  • Added a live reporting module for sending backup progress to a URL
  • Added pluggable PAR2 parity module for error-correction of remote volumes, thanks @JamBalaya56562
  • Improved TLS certificate validation and fallback to OS-default validator
  • Locked the server database with a busy timeout to prevent concurrent access issues
  • Improved metadata update handling to better show remote versions
  • Guarded the compact operation against race conditions
  • Fixed database inconsistency after shared metadata delete
  • Avoid secondary transaction errors after repair failure, thanks @JamBalaya56562
  • Delete the incomplete recreate database when the remote is empty, thanks @JamBalaya56562
  • Stabilized ZipFallback database cleanup on Windows, thanks @JamBalaya56562
  • Reset restore rendezvous barriers per operation, thanks @JamBalaya56562
  • Fixed test-filters ignoring --source from a parameters file, thanks @JamBalaya56562
  • Removed redundant cancel button from restore progress, thanks @JamBalaya56562
  • Do not attach stack traces to known-problem path warnings, thanks @JamBalaya56562
  • Made CLI find search all backup versions by default, thanks @JamBalaya56562
  • Use effective dbpath for show-log and delete-db endpoints, thanks @JamBalaya56562
  • Reporte script execution in operation status, thanks @JamBalaya56562
  • Added commandline target URL done action, thanks @JamBalaya56562
  • Returned non-zero exit code from server-util run on backup failures, thanks @JamBalaya56562
  • Added DfsrPrivate to default excludes, thanks @JamBalaya56562
  • Escaped Windows commandline percent expansion, thanks @JamBalaya56562
  • Updated SQLitePCLRaw e_sqlite3 library, thanks @JamBalaya56562
  • Stabilized Windows ACL restore test, thanks @JamBalaya56562
  • Updated Swashbuckle to patched OpenApi, thanks @JamBalaya56562
  • Delayed Windows service startup, thanks @JamBalaya56562
  • Validated retention-policy format instead of crashing on missing colon, thanks @TowyTowy
  • Allowed machine-id override to fix mismatched ids for console connection and backup reports
  • Added endpoints for removal of versions and purge-broken files
  • Reported number of zero-length metadata entries
  • Fixed uninstall issue with delay-start service
  • Updated all localizations, thanks to all translators

UI updates

  • Option to start a managed backup from the home page.
  • Support for deleting versions from the actions menu.
  • Support for invoking purge-broken-files from the actions menu.
  • Moved the database compact action from the main menu to the database page.
  • Resolved websocket reconnect race conditions and timer mismatch
  • Aligned the disconnected dialog countdown with the actual reconnect delay (5s)
  • Renamed the “System settings” panel heading to “Server settings” for consistency with the tab name
  • Fixed a search issue with advanced options
  • Resolved the schedule page next-time off-by-one day issue
  • Updated localizations, thansk to all translators

FWIW, I want to sincerely thank all Canary users for their courage and diligence which appreciatively helps hold Duplicati in my most favorite utility software service of which I’ve leveraged over several decades. I’m a GNU/Linux and Windows user and the ‘next stable release’ always excites me because the Canary user tested updates, their experienced feedback, @kenkendk timely efforts, along with the forum user feedback helping assure the next Stable update. Love & Light

Ok, sorry, already hating the new locked down folder.



I see nothing wrong so why won’t it start? This is Windows Server 2025 (domain joined).

Forgot to add, the tray icon was complaining first, so when that ran after the command I thought it was done, but the service won’t, which runs a local system.

Thanks for reporting this. I think it is a logic bug, that happens when you run the configure tool with elevated permissions. It assigns the current user (an administrator account) as the owner, and then the check fails because that owner is not in the permitted set (Administrator (group) or SYSTEM) once the service starts.

The screenshot does say “Owner: Administrator”, but that is not the SID that Duplicati finds as the folder owner. The SID that Duplicati reports looks like a local administrator SID.

I will update the configure tool to handle this with a --for-service option. For a fix, there are two ways to do it.

The simple fix is to run

Duplicati.WindowsService.exe secure-datafolder

This will restart the service and ask it to set the permissions. Since this runs in the service context, it will set the correct permissions.

Alternative is to edit the setup, remove the “Administrator” entry from the permission entries so it only has “SYSTEM” and “Administrators” (note the trailing s). Then change the Owner to be “SYSTEM” (or Administrators).

The problem is that inside the backup, there is 1 file where the metadata does not match up. You should be able to use the new “Purge broken files” UI to list and remove/fix the entry that cannot be mapped.

Thanks, using Duplicati.WindowsService.exe secure-datafolder the problem was sorted.

As for the backup issue, I’ll try the purge shortly, but it reminded me about something else - pausing pauses everything? As I was going to run the purge I remembered the backups were paused for some time, so I therefore cannot run anything unless I resume and then the missed jobs all run. It’s quite annoying that through the UI I cannot run anything while paused and have to resume which can mean waiting a long time for something to fail just to be able to try and fix it, potentially making things worse. I think the “pause” should be more granular e.g. add a separate pause button to each job

It usually made sense to do the added lockdown by setting explicit permissions.
I’m unconvinced inherited secure permissions should be rejected by new check:

Duplicati.Library.Interface.UserInformationException: The data folder 'C:\Duplicati\duplicati-2.3.0.107_canary_2026-07-13-win-x64-gui\RUN' does not have secure permissions (the folder inherits permissions from its parent). This is a security risk because the database and configuration may contain sensitive data, and an attacker who can write to the folder could inject a malicious database or configuration. This folder already existed before Duplicati started and does not have the expected restricted permissions, which can mean it was created by another (possibly malicious) user and may contain a malicious database or configuration. Duplicati does not automatically adopt an existing folder’s permissions. Verify the folder was created by Duplicati (not an attacker), then restrict its permissions using the ‘configuretool secure-datafolder’ command (or manually). Alternatively, run with --allow-insecure-datafolder to bypass this check if you understand the risks.

I have a weird multiple-Duplicati install using a RUN.bat in a RUN folder I make, which is also the server-datafolder. If I could secure the parent C:\Duplicati, allowing inherited secure permissions may make it easier to zip-install Duplicati.

That was kind of a weak claim, as not trusting pre-populated folders hits as well.

EDIT 1:

The directions to run configuretool are likely wrong for Windows, where tool is
Duplicati.CommandLine.ConfigureTool. Can command directions vary by OS?

EDIT 2:

Since RUN.bat adds options, for now I also add --allow-insecure-datafolder.
The script not only lives there, but also does a cd to it before launching Duplicati, allowing using simple filenames (e.g. for logs), instead of ever-relocated full paths.

EDIT 3:

“Duplicati does not automatically adopt an existing folder’s permissions.” was hard to understand, and I wondered if “permissions” meant “contents” (per words before it). Maybe “adopt” meant “accept” if it truly refers to folder permissions and not content.

I’m willing to change permissions (even explicit ones if inherited remains forbidden), however I’m not sure if there’s an option to say that the new folder contents are OK.

Running configuretool from script might be possible. I haven’t tested it that way yet.

EDIT 4:

Tested without --allow-insecure-datafolder on Duplicati start. Noisy but works.

Using data folder: C:\Duplicati\duplicati-2.3.0.107_canary_2026-07-13-win-x64-gui\RUN
The data folder does not have restricted permissions.
Current state: the folder inherits permissions from its parent

Warning: if someone created this folder, running this command may inadvertently give unwanted access to this system.
Before proceeding, verify that the data folder was created by Duplicati and not by an attacker,
as restricting the permissions will grant the current user, SYSTEM and Administrators full control over the folder.

Restricted permissions applied to the data folder.
Only the current user, SYSTEM and Administrators can now access the folder.

As an option quibble, notice inconsistent spelling of datafolder and data-folder.

start /b ..\Duplicati.CommandLine.ConfigureTool secure-datafolder --data-folder %CD% --apply
start /b ..\Duplicati.GUI.TrayIcon.exe --server-datafolder="%CD%"

EDIT 5:

Inconsistency as seen in the help. Environment variables favor DATAFOLDER.

Usage:
  Duplicati.CommandLine.ConfigureTool secure-datafolder [options]

Options:
  --data-folder <data-folder>  Path to the Duplicati data folder (defaults to standard location)

New behavior is the old behavior plus inability to manually correct it:

Basically, I can’t Submit. Nothing on net. Haven’t played with it much.

EDIT 1:

EDIT 2:

Workaround is to use old UI via Settings to do import. It gets July 19.
Go back to new UI and look at what job got for Schedule. It’s July 18.

What came from the server to show that:

        "Time": "2026-07-19T12:20:00Z",
        "Repeat": "1D",
        "LastRun": "2026-07-18T12:20:00Z",

Good news is it seems less prone to run due to a past-due schedule.
Bad news is trying to set a further future date (July 20) is rejected too.

EDIT 3:

Also rejects older past dates. Changing to 1 hour repeat, same result.
Visual appearance is Submit button is ignored (so check in dev tools).

EDIT 4:

Bug seems new for this Canary. 2.3.0.106 lets me schedule a backup.
2.3.0.107 new UI basically doesn’t, though it did let me do 13:00 Daily.

Trying “Store configuration with backup” announced in 2.3.0.105. First try said:

Error

Invalid manifest detected, the field Blocksize has value 102400 but the value 1048576 was expected. This could be a configuration issue, or be caused by mixing files from different backups.

I think the problem is that backup began in 2.0 era, so is still using old blocksize.
There is no explicit --blocksize given, but I think DB knows what it has to use.
I think recreating the DB also figures this out somehow from the destination data.
“Import from destination” feature seems confused. In terms of reporting, both the Alerts and Notifications icons turn red (not used to seeing that) and give the error.

EDIT 1:

The claim about manifest is correct, but the expectation is off. With DB, one has

but I suspect a DB isn’t involved in this case. It’d still be nice if it figured things out.

EDIT 2:

Another way to get error is by explicit blocksize option, e.g. for an oversize 5MB.

I think the DB recreate adaptive code is the call to UpdateOptionsFromManifest, basically taking what the Destination has, rather than expecting the current default.

Unfortunately this now breaks the tray icon when I logged back into the server I fixed, it now complains about the security again but the service is still fine.

Well… it pauses the scheduler. So you should be able to run the list-broken-files and purge-broken-files commands as they bypass the scheduler.

You are correct that there is nothing directly insecure about inherited permissions. However, the permissions can change at any time and needs to be verified. My worry is that we will create holes or bugs if we start to attempt a full parsing and validation of all inherited permissions.

I usually try to write both, but I see I forgot in the release notes.

Maybe that should have had another read. Previous versions would “adopt” existing an datafolder by setting permissions on it. This version no longer does this and will not “adopt” (or accept) an existing folder with incorrect permissions.

Next version will feature a --quiet option.

Good catch. The configure tool will use --datafolder going forward.

Thanks for reporting, it is fixed in source.

Yes, that is exactly it. When reading the remote file, Duplicati will compare the local (default) settings to the settings recorded in the manifest. If these do not agree you get an exception message saying “invalid manifest”.

For the next version, the manifests are fully ignored when restoring the configurations.

I think your setup is launching the TrayIcon with --server-datafolder=... ?

If you prefer that, then you need to add --allow-insecure-datafolder to both the TrayIcon and the service. (Alternatively run TrayIcon as Administrator which is a hassle).

The reason for this is that you need two different accounts to access the folder, which is something we do not currently have a secure way of doing. The alternative is to remove the --server-datafolder argument from the TrayIcon and then rely on the default credential manager. When you do this, the TrayIcon will start up in a disconnected state, and you can right click it to set the password. If you choose to save it, it will be stored in the Windows Credential Manager, and remembered for the next startup.

If you prefer the datafolder approach, the next release will allow you to place the previous insecure-permissions.txt file into the Duplicati install folder, so it automatically applies to both server and TrayIcon.

Thanks, anything more I can try for my broken backup?

I was mostly worried about program directions to user being for the wrong OS:

There is no such program on my Windows system. This will confuse the users.

meant directions to the user on what command to run (which varies by the OS).
If dynamic is hard, at least explaining both names is a crude but simple method.

This is equally true of explicit permissions, isn’t it? Aren’t they always verified?

Although a slippery slope may occur, this icacls output looks trivially different:

Accepted:
. NT AUTHORITY\SYSTEM:(OI)(CI)(F)
  BUILTIN\Administrators:(OI)(CI)(F)
  PC\User:(OI)(CI)(F)

Rejected:

. NT AUTHORITY\SYSTEM:(I)(OI)(CI)(F)
  BUILTIN\Administrators:(I)(OI)(CI)(F)
  PC\User:(I)(OI)(CI)(F)

The rejected one is inherited, and is actually a subfolder of the accepted folder.
Without knowing coding way, I don’t see how rejected one is hard to recognize.
OTOH I’m not sure how often the problem will come up for others. Time will tell.

Thanks. If that wasn’t there I would have considered redirecting output to NUL.

This is nice (I have a long run-script-after) but new UI shows the raw constant.
Legacy UI converts RunScript_Running to friendly form and has translations:
'RunScript_Running': gettextCatalog.getString('Running script …'),
I’d guess ngclient status-states.constant.ts needs to learn latest addition.

2.3.0.108 reverts to the old behavior, meaning it’s a day early but is fixable.
Maybe I’ll have to open third copy of the below issue to get it on the queue:

EDIT 1:

Wound up putting a comment in 469, so it can be reopened if you prefer that.

EDIT 2:

18 days and no reopen, so I filed new issue.