🚀 New Tool Release: duplistatus – A Self-Hosted Duplicati Dashboard with API Support

Hello Duplicati Community!

I’m excited to share a new open-source project I’ve been working on called duplistatus , a web-based dashboard for monitoring Duplicati backups across multiple machines. You can find the source code on GitHub here:
:backhand_index_pointing_right: GitHub - wsj-br/duplistatus

:brain: Why I Built This

After switching to Duplicati as my primary backup solution for home servers, I explored existing dashboards like the official Duplicati dashboard and Duplicati Monitoring. However, I had two main requirements:

  1. Self-hosted — I wanted full control over where the data lives.
  2. API access — So I could integrate it with Homepage , which I use as a central dashboard for my home lab.

Connecting directly to each Duplicati server was tricky due to authentication limitations, so I decided to build a centralized service that would pull status from all my Duplicati instances and expose a clean API.

Since I was also experimenting with AI-assisted development, I decided to try building this entire app using AI tools. The full process is documented in the repo under HOW-I-BUILD-WITH-AI.md.


:bar_chart: Features

  • Overview : Real-time display of backup statuses across all machines
  • Machine Details : View detailed backup history per machine
  • Data Visualization : Interactive charts showing backup metrics over time
  • Log Collection : Pull all logs available directly from existing Duplicati servers, so you can populate the database with your historical data.
  • Dark/Light Theme : Toggle between themes for better readability
  • API Access : Expose backup status via RESTful endpoints (ideal for Homepage or other integrations)
  • Easy Deployment : Run via Docker (images available on Docker Hub and GitHub Container Registry)

:wrench: Development & Customization

If you’d like to contribute, customize, or debug the tool locally, check out the DEVELOPMENT.md file in the repository. It includes everything you need to know to get started with running the app in development mode.


:spouting_whale: Deployment Options

You can deploy duplistatus easily using Docker, see instruction in the README.mdfile in the respository.


:handshake: Feedback Welcome!

I’d love to hear your thoughts, suggestions, or any issues you may encounter. Contributions are also very welcome! Whether you’re using Homepage, another dashboard, or just want a cleaner view into your Duplicati backups, I hope this tool proves useful.

Let me know what you think — thanks for reading and happy backing up!

:link: Project Link: GitHub - wsj-br/duplistatus

9 Likes

Thanks for this, looks really good - I’ll see if I can deploy for my environment:

  • Podman rather than Docker - I shouldn’t have an issue translating the container deployment to Podman, I’ve done it enough times now
  • Homarr rather than Homepage - I was using Homepage until quite recently but tired of the management through yaml, but this isn’t a priority for me anyway
  • Mix of Windows & Linux machines running Duplicati - I’m usually on a canary build, as I enjoy living dangerously, so hopefully I wont hit too many issues
  • Zabbix for monitoring, so I can dabble with the API access for integration

How can I test the “upload” from within Duplicati, is there some way to simulate it from the command-line?

I have the container running and can see the website so the IP and port are exposed correctly:

But when I add the lines to my default settings then run a backup job (I tried both a Windows and Linux machine), I always get the same 4 warning loggeds and nothing happens on server:

Log data:
2025-06-02 12:36:57 +02 - [Warning-Duplicati.Library.Modules.Builtin.SendHttpMessage-HttpResponseError]: HTTP Response request attempt 1 of 3 failed for: http://192.168.1.70:9666/upload
System.Net.Http.HttpRequestException: Response status code does not indicate success: 404 (Not Found).
   at System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode()
   at Duplicati.Library.Modules.Builtin.SendHttpMessage.<>c__DisplayClass62_0.<<SendMessage>b__0>d.MoveNext()
--- End of stack trace from previous location ---
   at Duplicati.Library.Utility.RetryHelper.<>c__DisplayClass2_0.<<Retry>b__0>d.MoveNext()
--- End of stack trace from previous location ---
   at Duplicati.Library.Utility.RetryHelper.Retry[T](Func`1 action, Action`2 errorCallback, Int32 maxRetries, TimeSpan delay, CancellationToken token)
2025-06-02 12:36:58 +02 - [Warning-Duplicati.Library.Modules.Builtin.SendHttpMessage-HttpResponseError]: HTTP Response request attempt 2 of 3 failed for: http://192.168.1.70:9666/upload
System.InvalidOperationException: The request message was already sent. Cannot send the same request message multiple times.
   at System.Net.Http.HttpClient.CheckRequestMessage(HttpRequestMessage request)
   at System.Net.Http.HttpClient.CheckRequestBeforeSend(HttpRequestMessage request)
   at System.Net.Http.HttpClient.SendAsync(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationToken cancellationToken)
   at Duplicati.Library.Modules.Builtin.SendHttpMessage.<>c__DisplayClass62_0.<<SendMessage>b__0>d.MoveNext()
--- End of stack trace from previous location ---
   at Duplicati.Library.Utility.RetryHelper.<>c__DisplayClass2_0.<<Retry>b__0>d.MoveNext()
--- End of stack trace from previous location ---
   at Duplicati.Library.Utility.RetryHelper.Retry[T](Func`1 action, Action`2 errorCallback, Int32 maxRetries, TimeSpan delay, CancellationToken token)
2025-06-02 12:36:59 +02 - [Warning-Duplicati.Library.Modules.Builtin.SendHttpMessage-HttpResponseError]: HTTP Response request attempt 3 of 3 failed for: http://192.168.1.70:9666/upload
System.InvalidOperationException: The request message was already sent. Cannot send the same request message multiple times.
   at System.Net.Http.HttpClient.CheckRequestMessage(HttpRequestMessage request)
   at System.Net.Http.HttpClient.CheckRequestBeforeSend(HttpRequestMessage request)
   at System.Net.Http.HttpClient.SendAsync(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationToken cancellationToken)
   at Duplicati.Library.Modules.Builtin.SendHttpMessage.<>c__DisplayClass62_0.<<SendMessage>b__0>d.MoveNext()
--- End of stack trace from previous location ---
   at Duplicati.Library.Utility.RetryHelper.<>c__DisplayClass2_0.<<Retry>b__0>d.MoveNext()
--- End of stack trace from previous location ---
   at Duplicati.Library.Utility.RetryHelper.Retry[T](Func`1 action, Action`2 errorCallback, Int32 maxRetries, TimeSpan delay, CancellationToken token)
2025-06-02 12:36:59 +02 - [Warning-Duplicati.Library.Modules.Builtin.ReportHelper-ReportSubmitError]: Failed to send message: System.InvalidOperationException: The request message was already sent. Cannot send the same request message multiple times.

System.InvalidOperationException: The request message was already sent. Cannot send the same request message multiple times.
   at System.Net.Http.HttpClient.CheckRequestMessage(HttpRequestMessage request)
   at System.Net.Http.HttpClient.CheckRequestBeforeSend(HttpRequestMessage request)
   at System.Net.Http.HttpClient.SendAsync(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationToken cancellationToken)
   at Duplicati.Library.Modules.Builtin.SendHttpMessage.<>c__DisplayClass62_0.<<SendMessage>b__0>d.MoveNext()
--- End of stack trace from previous location ---
   at Duplicati.Library.Utility.RetryHelper.<>c__DisplayClass2_0.<<Retry>b__0>d.MoveNext()
--- End of stack trace from previous location ---
   at Duplicati.Library.Utility.RetryHelper.Retry[T](Func`1 action, Action`2 errorCallback, Int32 maxRetries, TimeSpan delay, CancellationToken token)
   at Duplicati.Library.Utility.RetryHelper.Retry(Func`1 action, Action`2 errorCallback, Int32 maxRetries, TimeSpan delay, CancellationToken token)
   at Duplicati.Library.Modules.Builtin.SendHttpMessage.SendMessage(HttpClient client, SendRequestType target, String subject, String body)
   at Duplicati.Library.Utility.Utility.Await[T](Task`1 task)
   at Duplicati.Library.Modules.Builtin.SendHttpMessage.SendMessage(String subject, String body)
   at Duplicati.Library.Modules.Builtin.ReportHelper.OnFinish(IBasicResults result, Exception exception)

Nothing shows on the log of the container:

These are the settings I added:

--send-http-url=http://192.168.1.70:9666/upload
--send-http-result-output-format=Json
--send-http-log-level=Information

My jobs also have the SMTP options to send job logs and they are working, so perhaps there’s a conflict there?

I found the problem re-reading the documentation:

vs

image

Now I got something:

1 Like

Seems the backup download option might not support HTTPS connections:

Error collecting backups: TypeError: fetch failed
    at async u (.next/server/app/api/backups/collect/route.js:1:2124) {
  [cause]: [Error [SocketError]: other side closed] {
    code: 'UND_ERR_SOCKET',
    socket: {
      localAddress: '10.88.0.10',
      localPort: 42628,
      remoteAddress: '192.168.1.10',
      remotePort: 8200,
      remoteFamily: 'IPv4',
      timeout: undefined,
      bytesWritten: 295,
      bytesRead: 0
    }
  }
}

HTTPS might need to be added as something we can set up for the container, although for me I can proxy it via HAProxy to make it secure but I’d rather not have to do that.

I’m not sure the machine stats are coping very well with multiple back jobs on a single machine:

IMO, setting up nginx with certbot is something very simple and almost a standard. Other projects make this even simpler (e.g. caddy).

Thank for spoting this. I will update the README.md with the right instructions.

1 Like

@Taomyn: As I don’t use the HTTPS in my duplicati instances, I didn’t implement it. But I will implement the log collection using https in the next version.

Also, I will think in a way to implement the dashboard to serve HTTPS, need to investigate the nginx/certbot or caddy as suggested by @SamSirry.

I just consolidate all the backups (backups and runs) of each server in the table, showing in the frontpage the latest received. The summary statistics also is consolidated.

What do you think should be a good behaviour? I can try to implement another way to present it, probably adding a level down server>backup>runs, today is just server>backups&runs.

1 Like

I meant that the people implementing the solution should take care of the TLS setup, not you the developer.

Basically it goes like this:

  1. Open firewall ports 80 and 443.
  2. Setup duplistatus with love.
  3. (On Linux) do a apt install nginx python3-certbot-nginx to install the necessary packages.
  4. Verify or update nginx’s configuration (under /etc/nginx/) (optional for a single local hosted app)
  5. Run certbot --nginx

However, you @wsj-br as a developer, if you wanna take the trouble, you can add the option to configure your webserver with the certs generated by certbot. The user will do:

  1. Open firewall ports 80 and 443.
  2. Setup duplistatus with love.
  3. apt install certbot.
  4. Run certbot certonly
  5. Use the path of the generated cert and key in duplistatus’s configuration.

The paths look something like so on my Debian:
tls-cert=/etc/letsencrypt/live/host.example.com/fullchain.pem
tls-key=/etc/letsencrypt/live/host.example.com/privkey.pem

Understood. The SSL support is straightforward; the complicated part is managing the certificates (self-signed, internal CA, Let’s Encrypt, etc.). Your first option is better. I will add these steps to the documentation. Thank you for all the details.

By the way, I just released a new version. Now, the data collection supports SSL if the Duplicati server is configured, including support for CA and self-signed certificates. I have also added better support for multiple backups on the same server.

Thanks again for the feedback and help.

@Taomyn: I released a new version v0.4.0 with the SSL support to collect the logs. It supports CA or self-signed certificates. Also I added a better support for multiple backups in the same server.

I hope this helps.

Thanks again for the feedback.

Hi, thank you for all the changes, I have been updating with your previous releases just been a bit busy to say much more, but I will try this one later today and report back. I can see this being a very useful tool for managing my backups.

I updated my container (would be useful to see the version displayed somewhere to be sure of what I deployed), and everything continues to work.

I tested the backup log collection on one server using SSL+self-signed, and that worked perfectly.

I still see grey for error’d jobs, which is why I mention about showing the version, as previously I couldn’t tell if I really had the latest version


In English, this should be “Total Backed Up Size”

“Backup” and “Back up” are weird in English - as I have always understood it in the 40yrs in IT, you use for example “A backup job”, “It backed up 100TB”, “I need to back up my server”, “We have 100 backups in storage” etc, so to me “Total Backuped Size” looks wrong. I see this a lot as my fellow IT colleagues, a mix of French, Belgian and German, all do the same.

I will test further as I have time, but great job :+1:

FYI, in case it’s of interest as I use Podman under Fedora rather than plain Docker, these are my commands for building the Pod:

podman pod create --name Duplistatus --publish 9666:9666/tcp

podman create --name duplistatus --pod Duplistatus --user root --no-healthcheck -v /root/duplistatus_home/data:/app/data --env TZ=Europe/Luxembourg --env NODE_ENV=production --env PORT=9666 --env NEXT_TELEMETRY_DISABLED=1 ghcr.io/wsj-br/duplistatus:latest
  • I don’t use the health check as I have other ways to do this, plus I run the pod+container using systemd
  • As for running root, I just always do this as I really don’t care

Not sure if this forum, or the GH issues page is the correct place to log this, but when attempting to fetch backups, the following error is raised:

duplistatus  | Error collecting backups: SyntaxError: Unexpected end of JSON input
duplistatus  |     at JSON.parse (<anonymous>)
duplistatus  |     at IncomingMessage.<anonymous> (.next/server/app/api/backups/collect/route.js:1:1904)

logs are the container logs.

I am running both on same machine (both sides set to localhost) and I am getting ECONNREFUSED so Duplistatus is reading nothing. How do I resolve this? Host name is lleft blank but enabled so I can use either localhost or 127.0.0.1, but both get that ECONNREFUSED error.