Self-hosted service
plutonhq/pluton avatar
plutonhq/pluton

Pluton: Restic and Rclone behind a self-hosted backup UI

A modern, self-hosted backup solution for secure, encrypted backups across local and cloud storage.

849 stars36 forksTypeScriptApache-2.0

At a glance

What is it?
Pluton is a self-hosted backup manager that puts a web interface in front of Restic for the backup work and Rclone for storage connectivity, so scheduling, retention, restore and replication live in one screen. The engineering worth knowing is in the packaging: the Docker image wraps prebuilt executables rather than building from source, which makes the build order a real dependency.
Who is it for?
Pluton fits a homelab or small-team operator who wants encrypted incremental backups with a UI instead of cron lines, and who is already using Restic and Rclone and would rather not orchestrate retention and replication by hand. It does not fit a build pipeline that expects a Dockerfile to be self-contained, because the image depends on artifacts produced by a separate executable build step.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 32 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 11, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Two upstream tools do the work, the web interface does the rest

Pluton is a backup management platform rather than a backup engine. Restic provides the secure incremental backups, with encryption, compression and retention policies, and Rclone provides the connectivity to cloud storage destinations. Both are credited in the README with their licences, Restic under BSD 2-Clause and Rclone under MIT.

Everything above that layer is Pluton's own: flexible scheduling for automated jobs, fine-grained retention policies, replication of content to multiple cloud destinations so you can build a 3-2-1 plan, restore or download of snapshot data in a few clicks, and end-to-end encryption from the local machine to the cloud destination. Storage coverage is quoted as 70+ backends, powered by rclone.

The operational surface is unusually complete for a small project: real-time progress tracking, application and backup logs readable from the UI, automatic retry with customisation options when a job fails, notifications to email, Slack, Discord and push notifications through NTFY, and the ability to run scripts before and after a backup runs.

The image is debian-slim plus restic and rclone copied in

The Dockerfile starts from `debian:bookworm-slim` and installs only four runtime packages: `ca-certificates`, `openssl`, `fuse` and `wget`. There is no toolchain and no compiler, because nothing is compiled inside the image.

Instead, `dist/executables` is bind-mounted to `/tmp/executables` and the architecture is chosen from `TARGETARCH`, mapping `amd64` to `x64` and `arm64` to `arm64`, with an explicit error for anything else. The build then copies `pluton-linux-${ARCH_SUFFIX}` into `/app`, marks `pluton` executable, and refuses to continue if `better_sqlite3.node` did not arrive, which is a cheap guard against a silently broken native module.

The last step is the interesting one: `restic` and `rclone` are copied out of the app bundle into `/usr/local/bin`, made executable there, and then removed from the app directory as duplicates. That means the image ships two copies of the application tree during the build and one set of tools at the end.

One detail to note for release tooling: the Dockerfile declares `ARG APP_VERSION=0.0.1`, while `package.json` is at `0.20.0`, so the version baked into the image metadata is not the version of the code unless you pass the build argument yourself.

You must build the executables before you build the image

The first two comment lines of the Dockerfile state the dependency: it uses pre-built binaries from `scripts/build-executables.js`, and you should run that script before building the image. The compose file makes the same assumption quietly, since its `build:` block is commented out by default and it pulls `plutonhq/pluton:latest` from Docker Hub instead.

The executable build is defined in `package.json`. `@yao-pkg/pkg` at `6.20.0` does the packaging, with `backend/public/**/*` and `backend/drizzle/**/*` declared as assets and five targets: `node24-win-x64`, `node24-linux-x64`, `node24-linux-arm64`, `node24-macos-x64` and `node24-macos-arm64`, all written to `dist/executables`. Those same targets are what produce the desktop installers for Windows, macOS and Linux that the download page offers, so one script feeds both the container and the desktop builds.

A local build is narrowed by flag: `build:executables:local` passes `--platform win-x64,linux-x64`, and `build:exe:local` goes through `installers/windows/build-installer.js`. A `Dockerfile.source` sits next to the main one in the repository root, which is the path for building from source instead of wrapping binaries.

The README's compose snippet is truncated, the file in the repo is not

The README shows a compose file that stops mid-line, ending on a partial Windows path. The committed `docker-compose.yml` is the fuller version, and it is the one to read before you write your own.

What it actually contains: a service named `pluton` running `plutonhq/pluton:latest` with `container_name: pluton-backup` and `restart: unless-stopped`, a port mapping of `${SERVER_PORT:-5173}:${SERVER_PORT:-5173}`, and a single named volume `pluton-data:/data` that holds the database, config and logs. Everything else in the volume section is commented out, including examples for documents and photos mounted read-only, Docker volume data mounted read-only, and a writable local backup destination.

That last example carries the most useful comment in the file: a backup destination has to be writable, so leave off `:ro`, and in Pluton you select the container path such as `/mnt/backups`, not the host path. Getting that backwards is the kind of mistake that only shows up at the first restore.

The optional port example says 7173 while the default is 5173

The environment story is short enough to state precisely. Three variables are required: `ENCRYPTION_KEY`, described in the compose file as the encryption key for restic and rclone snapshot encryption, plus `USER_NAME` and `USER_PASSWORD`. The compose file adds a practical instruction for generating them: secure random strings, with a minimum of 12 characters for the first three.

The README's `.env` example shows the same three with placeholder values, then an optional override section that sets `SERVER_PORT=7173`. The port mapping in the compose file defaults to `5173` on both sides, so an operator who copies the optional line verbatim ends up on 7173 rather than the documented default, and nothing warns them.

A repository-level `.env.template` exists alongside the compose file, and `docker-compose.dev.yml` sits next to the production one, so the development path is separate rather than a flag on the same file.

package.json carries an absolute path from one maintainer's drive

The `build:appimage:local` script is a Windows-specific command with a hard-coded path:

code
wsl -e bash -c "cd /mnt/f/JS/apps/pluton/codebase/pluton-app/pluton/installers/linux && ./build-appimage.sh all pluton"

That `/mnt/f/JS/apps/pluton/...` prefix only exists on the machine of whoever wrote it, so the script cannot run anywhere else without editing. It is the kind of leftover that is harmless in CI and blocking for a contributor, and it sits next to three scripts that are properly portable.

The rest of the manifest is in good order. `prepare` installs git hooks through `scripts/setup-git-hooks.js`, `build` delegates to turbo, and `dev` runs the backend and frontend concurrently through `concurrently`, each filtered by package name, `@plutonhq/core-backend` and `@plutonhq/core-frontend`. `syncpack` is a dev dependency, which is how the workspace keeps dependency versions aligned across packages.

The declared package manager is `[email protected]`, with `turbo` running the build graph across `backend` and `frontend`.

The monorepo declares its workspaces twice, and the tags carry a prefix

The root `package.json` lists `workspaces` as `backend` and `frontend`, in the npm style, while the same file declares `packageManager: [email protected]` and the repository also contains `pnpm-workspace.yaml`. Under pnpm the `workspaces` field is not what defines the workspace, so the YAML file is the authority and the JSON field is redundant. Neither of them covers `installers/` or `scripts/`, which stay outside the package graph.

Versioning runs through release automation rather than by hand. The repository has `release-please-config.json`, a `.release-please-manifest.json` and a `CHANGELOG.md`, and the tags follow that convention: `pluton-v0.20.0` on 2026-09-09, `pluton-v0.19.0` on 2026-08-30 and `pluton-v0.18.3` on 2026-08-09, with the last push to `main` on 2026-09-09, the same day as the 0.20.0 tag.

Installation comes in three shapes: desktop installers for Windows, macOS and Linux from the download page, the compose file above, and documented steps for headless Linux servers. The project is Apache-2.0 licensed, with the site at usepluton.com and the documentation at docs.usepluton.com.

Editorial conclusion

Pluton fits a homelab or small-team operator who wants encrypted incremental backups with a UI instead of cron lines, and who is already using Restic and Rclone and would rather not orchestrate retention and replication by hand. It does not fit a build pipeline that expects a Dockerfile to be self-contained, because the image depends on artifacts produced by a separate executable build step. Check three things before you deploy it: that you have run `node scripts/build-executables.js` before `docker build`, that your backup destination is a writable mount with Pluton pointed at the container path rather than the host path, and that your `ENCRYPTION_KEY` is at least 12 characters and stored somewhere you will still find it, since it is the key to every snapshot. Also decide your port deliberately: the default is 5173 even though the README's optional example shows 7173.

Frequently asked questions

What is pluton, the backup tool?

Pluton is a self-hosted backup management platform for automated incremental backups across your own storage destinations. It wraps Restic for the encrypted, compressed backup work and Rclone for cloud storage connectivity, and puts scheduling, retention policies, replication, restore and logs behind a web interface.

Which environment variables does Pluton require?

ENCRYPTION_KEY, which the compose file describes as the encryption key for restic and rclone snapshot encryption, plus USER_NAME and USER_PASSWORD. The compose file recommends secure random strings with at least 12 characters for the first three. SERVER_PORT is optional and the port mapping defaults to 5173 on both sides.

How many storage backends can Pluton back up to?

70 or more, powered by rclone. Pluton can also replicate content to multiple cloud storages to build 3-2-1 plans, and a local directory can serve as a destination, in which case the mount must be writable and Pluton is pointed at the container path rather than the host path.

Does Pluton tell me when a backup fails?

Yes. Event notifications go to email, Slack, Discord and push notifications through NTFY, automatic retry logic re-runs failed backups with customisation options, progress is tracked in real time, and both application and backup logs are viewable from the interface.

How do I build the Pluton Docker image?

Run node scripts/build-executables.js first, because the Dockerfile copies prebuilt binaries from dist/executables through a bind mount rather than compiling anything. It selects pluton-linux-x64 or pluton-linux-arm64 from TARGETARCH, verifies that better_sqlite3.node was copied, then places restic and rclone in /usr/local/bin.

Official sources

  1. License: Apache-2.0
  2. plutonhq/pluton on GitHub
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/plutonhq-pluton.svg)](https://hysenlabs.com/projects/plutonhq-pluton)