Personal project, Since August 2026
Backhaul
A scheduled backup tool that uploads local folders to Infomaniak kDrive, with configurable retention per folder. It runs in a Docker container.
Details
- Role
- Design and development
- License
- Open source, ISC
- Distribution
- Docker image on GHCR
Context
Local backups, but no off-site copy.
On my home server, the apps already make their own backups: zip files dropped in a folder. But those files stay on the same machine as the data they protect.
I wanted a containerised app that sends them to Infomaniak kDrive, set up with a single config file, without piling them up forever. It was also a chance to see a project through from start to finish: tests, Docker image, published releases.
How it works
- ScheduleA cron expression starts the backup, and optionally the container start.
- SelectionThe files of each source folder, except hidden files and recently changed ones.
- UploadEach file goes to its kDrive folder, unless it is already there with the same hash.
- RetentionEach destination keeps only its most recent files.
- SummaryA summary in the logs, and an exit code that says whether everything succeeded.
One container, two read-only volumes
The container only gets its configuration and the folders to back up, mounted read-only: Backhaul never changes local files.
Each folder is handled on its own. A missing source, such as an unmounted volume, is reported in the logs without blocking the others.
# compose.yml
services:
backhaul:
image: ghcr.io/tremblets/backhaul:latest
restart: unless-stopped
stop_grace_period: 5m
environment:
TZ: Europe/Zurich
volumes:
- ./config.yml:/config/config.yml:ro
- ./data:/data:ro
Choices
Never a half-written file
A file changed too recently waits for the next run. Its age comes from the newer of its mtime and ctime, which also covers copies that keep the original date.The date comes from the file name
Retention sorts files by the timestamp in their name, not by upload date: a late upload, after an outage, does not distort the order.Stopping deletes nothing
On shutdown, uploads in progress finish, no new one starts and retention is not applied. The rest goes up on the next run.A strict configuration
An unknown key, a duplicate destination or an invalid cron expression stop the app from starting, naming the faulty field.
Technologies
- Node.js
- Cron scheduler, kDrive uploads and a CLI for manual backups.
- Docker
- Image published on GHCR, run on the home server.
- Vitest
- Tests for retention, uploads and graceful shutdown.
- Zod
- Strict validation of config.yml at startup.