Add scheduled and manual backups

Backups are .tar.gz archives of a consistent database snapshot (VACUUM
INTO) plus the images folder. They run on a cron schedule, daily at 3:00
by default, and the newest N scheduled ones are kept (7 by default).
Manual backups are never pruned.

Settings → Backups offers schedule presets or a custom cron expression,
retention, "Back up now", and download/delete for each backup. A small
built-in parser handles standard 5-field cron syntax with names, ranges,
steps and @daily-style shorthands.

Backups go to CANON_BACKUP_DIR (default data/backups). Compose mounts a
separate canon-backups volume at /backups, overridable with
CANON_BACKUP_PATH.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
cdnutterandClaude Opus 5.5 committed 2026-09-30 14:30:40 -07:00
1 parent 72b49d709a
commit f3185ba7bd
18 files changed
+940 -10

No files matched your search

+5 -1
View File
@@ -18,6 +18,7 @@ import (
"time"
"github.com/cdnutter/canon/internal/auth"
"github.com/cdnutter/canon/internal/backup"
"github.com/cdnutter/canon/internal/config"
"github.com/cdnutter/canon/internal/imgcache"
"github.com/cdnutter/canon/internal/providers"
@@ -58,7 +59,8 @@ func run(log *slog.Logger) error {
if ver == "dev" {
ver = fmt.Sprintf("dev-%d", time.Now().Unix())
}
srv, err := server.New(cfg, st, images, log, ver)
backups := backup.New(cfg.BackupDir, images.Dir(), st, log)
srv, err := server.New(cfg, st, images, backups, log, ver)
if err != nil {
return err
}
@@ -70,6 +72,8 @@ func run(log *slog.Logger) error {
return err
}
go backups.Run(ctx)
go func() {
t := time.NewTicker(6 * time.Hour)
defer t.Stop()