Ping healthchecks.io when a check succeeds

When my backup succeeds, it pings healthchecks.io. But when my check succeeds it seems there is no way to ping healthchecks.io?

Ping when a check succeeds is much more useful thank pinging when a backup succeeds.

As a user, I want to be alerted as soon as a check fails. In my case, the backups have been succeeded for the past month, but for the last week, the checks failed. So if I didn’t check the web page, I would not have discovered it.

@saspus @gchen For example could the command check have an option --url-to-call-if-success where I would provide an URL and it will call this URL is the check is successful.

I have Duplicacy Web but I can also use the CLI directly. Any idea?

You could curl your url after running duplicacy in the wrapper script (assumingduplicacy returns non-zero value on check failure)

#!/bin/zsh    
duplicacy check && curl -L https://example.com/api/check-succeeded

For web UI you can then replace the duplicacy binary with this script, calling the original binary.

Duplicacy already supports pre- and post- operation scripts, like post-check, but web ui has no way to specify them, and afaik Duplicacy does not signal operation success into the script, so it’s a non-starter.

Either way, in the spirit of unix — one app for one job — the aforementioned approach is much preferred. You can do in your own scripts whatever you want, and not be limited by notification mechanisms duplicacy developer implemented.

Here’s the Python wrapper script I use with the Web UI to ping healthchecks.io and log messages: DuplicacyLog

(The wrapper is not a bash script because bash ignores the INT signal, which Duplicacy Web sends to abort the CLI.)

It’s because duplicacy is a background process, and SIGINT is intended to interrupt foreground process. Bash rightfully does not forward it to background processes.

But nothing prevents you from trapping the signal in your bash script and forwarding it to child duplicacy process manually (exactly as you do in your Python script). Or exec duplicacy instead of backgrounding it.

Either way is fine of course, nothing wrong with Python.

Thank you very much @saspus and @kgorlen for your help!

In my case, I plan to continue to have my backup from the GUI:

Plus on the side a script that would check and ping if success.

So I’m playing with the CLI. But when I run duplicacy check it asks for my backblaze IDs. I’m surprised because the GUI doesn’t ask each time for these IDs. Where is the GUI taking them from?

You can still do that. Fix the version of duplicacy CLI (in duplicacy.json), so that web UI does not download a new one, and replace duplicacy_linux_… whatever the name is with your script, that will call actual duplicacy and do other work you need. Duplicacy WebUI is just a fancy scheduler, all the work is done by the CLI

Credentials are stored encrypted in duplicacy.json (or keychain, if supported), and passed to duplicacy CLI via environment variables.

Very helpful answer!! And so I guess that my script should forward all the commands to the duplicacy binary?

Yes, something like this:

#!/bin/bash

# remove the first argument which is this script
shift

#pass remaining argumentes to duplicacy 
"/config/bin/duplicacy_original_binary" "$@"

# Get return code
exit_code=$?

if [ $exit_code -eq 0 ]; then
    # success
    curl -L https://example.com/success
else
    # fail
    curl -L https://example.com/fail
fi

exit $exit_code

Environment variables will be inherited by the child process automatically.

Note, I haven’t tried it, so there could be bugs.

Thank very much @saspus ! Will try it tonight!

To fix the version of the CLI, what should I do? Should I set “cli_version” in duplicacy.json and put the path towards the script?

Set it to the current version, e.g. 3.2.4, so it won’t download a newer one in the future; let it download that version if it hasn’t done it yet, and then rename the existing duplicacy_linux_x64_3.2.4 in the bin folder to e.g duplicacy_linux_x64_3.2.4_orig; name your script duplicacy_linux_x64_3.2.4, and call duplicacy_linux_x64_3.2.4_orig from there.

Okay, I see! Thank you very much!

If in the script I add echo "Hello" at the beginning, should I see the “hello” in the logs from the web UI?

Okay, I found the issue: I had the restart duplicacy.

I’ve successfully finished to implement the script:

#!/bin/bash

script_dir="$(dirname "$(readlink -f "$0")")"

healthcheck_url=""
args=()

shift

while [ $# -gt 0 ]; do
  if [ "$1" = "--healthcheck" ] && [ $# -gt 1 ]; then
    healthcheck_url="$2"
    shift 2
  else
    args+=("$1")
    shift
  fi
done

"$script_dir/duplicacy_linux_x64_3.2.3" "${args[@]}"

exit_code=$?

if [ -n "$healthcheck_url" ]; then
  curl -s "$healthcheck_url/$exit_code" > /dev/null || true
fi

exit $exit_code

that I put in ~/.duplicacy-web/bin/duplicacy_linux_x64_0.0.0 and in the duplicacy.json I put "cli_version": "0.0.0".

Now when I run backup / check / prune etc, I can add an extra --healthcheck [URL] and after the Duplicacy command is executed, the URL [URL]/[EXIT_CODE] is pinged.

I tested the failure cases and it works perfectly!

Thank you so much @saspus for the guidance!!

1 Like

There looks to be a bug: if --healthchecks is passed without the URL it will pass through to duplicacy. I think you shall remove && [ $# -gt 1] or fail if --healthcheck exists but $2 does not look like an URL.

It is fine if it is passed to Duplicacy. Duplicacy would fail. And I also assume that I call the script correctly.

It seems that since yesterday (when I replaced the Duplicacy bin by my script), I don’t have stats anymore for storage size, revisions, activities. Any idea?

I would (in order)

  • check stats folder to confirm stats are actually missing
  • verify the command line passed to duplicacy check (in the log), maybe some flags got omitted.
  • try setting the version to actual valid duplicacy version instead of zero, in case webui alters functionality based on version (e.g. maybe early Duplicacy did not output stats and webui does not expect any from ancient version 0.0.0)
  • try launching Duplicacy with exec, in case webui communicates with the child process via some signals that bash does not forward (in that case you would need to trap them in your script) or some other form of IPC (but this seems unlikely). (Note this is just as a test, because nothing will get executed after exec — bash would be gone)

Thanks. I removed the incorrect comment.

I’m checking the folder stats. In stats/schedules/Daily - Backup & Check.stats, I see:

...
    {
        "name": "Daily - Backup \u0026 Check",
        "type": "backup",
        "id": "Data@Backblaze",
        "status": "success",
        "log": "backup-20250312-040001.log",
        "start": "2025-03-12T04:00:01.259275363+01:00",
        "end": "2025-03-12T04:01:58.951620991+01:00",
        "jobs": null
    },
    {
        "name": "Daily - Backup \u0026 Check",
        "type": "check",
        "id": "",
        "status": "success",
        "log": "check-20250312-040158.log",
        "start": "2025-03-12T04:01:58.951699109+01:00",
        "end": "2025-03-12T04:02:49.505925059+01:00",
        "jobs": null
    },
    {
        "name": "Daily - Backup \u0026 Check",
        "type": "backup",
        "id": "Data@Backblaze",
        "status": "success",
        "log": "backup-20250312-090515.log",
        "start": "2025-03-12T09:05:15.446650428+01:00",
        "end": "2025-03-12T09:08:09.117549344+01:00",
        "jobs": null
    },
    {
        "name": "Daily - Backup \u0026 Check",
        "type": "check",
        "id": "",
        "status": "success",
        "log": "check-20250312-090809.log",
        "start": "2025-03-12T09:08:09.117729259+01:00",
        "end": "2025-03-12T09:09:15.708582312+01:00",
        "jobs": null
    }
]

So it seems that stats for this schedule has been saved?

For stats/storages/Backblaze.stats, I see:

...
"2025-03-09": {
        "total-size": 328309145600,
        "total-chunks": 112137,
        "pruned-chunks": 0,
        "pruned-revisions": 0,
        "status": "Checked",
        "repositories": {
            "Data": {
                "revisions": 29,
                "total-size": 319604916224,
                "unique-size": 319604916224,
                "total-chunks": 108336
            }
        }
    },
    "2025-03-10": {
        "total-size": 328883765248,
        "total-chunks": 112341,
        "pruned-chunks": 0,
        "pruned-revisions": 0,
        "status": "Checked",
        "repositories": {
            "Data": {
                "revisions": 30,
                "total-size": 320185827328,
                "unique-size": 320185827328,
                "total-chunks": 108542
            }
        }
    },
    "2025-03-11": {
        "total-size": 0,
        "total-chunks": 0,
        "pruned-chunks": 0,
        "pruned-revisions": 9,
        "status": "Checked",
        "repositories": {}
    },
    "2025-03-12": {
        "total-size": 0,
        "total-chunks": 0,
        "pruned-chunks": 0,
        "pruned-revisions": 0,
        "status": "Checked",
        "repositories": {}
    }
}