/features

Capabilities

Reference for what the self-hosted app implements today. Prefer this page over marketing blurbs when wiring backups, validate, webhooks, encryption, or MCP.

From → To transfers

from-to
sourceKinddestinationKindlocal|server|s3|peer

Every backup is a transfer between endpoints. Sources: local host, SSH server, or S3 object prefix. Destinations: local, server, S3, or Bro peer.

  • Directions include local↔local, local↔server, server↔server, and any side with S3 (S3 sources are path/prefix only).
  • Default for new jobs remains Server → Local under /backups/<server-slug>/<backup-slug>.
  • Destination uniqueness is per endpoint: local path, (destinationServerId, path), or (destinationS3ProfileId, prefix).

Source types

source-types
pathdocker_volumedatabaselazybackup_instance

Four source types. Destinations are always paths or object prefixes—never “to a volume.”

  • path — filesystem tree or S3 prefix; rsync preferred, scp fallback.
  • docker_volume — named volume on a source server or this host’s Docker socket; alpine packs .tar.gz (not a consistent live-DB backup).
  • database — Postgres / MySQL / MariaDB logical dump → .sql.gz via native client or docker exec (local or server). SQLite → .sqlite.gz (native file / sqlite3 .backup only).
  • lazybackup_instance — packs SQLite + age vault + SSH keys; optional age passphrase wrap (not the instance age key).

Server → Server (direct or relay)

server-to-server
ephemeral SSHrelay

When both ends are servers, prefer a direct path so data does not bounce through the backup host.

  • Installs an ephemeral SSH key on the destination so the source can rsync directly.
  • If the source cannot reach the destination, LazyBackup relays: pull to the host, then push.
  • S3 transfers always relay via the LazyBackup host. Ephemeral keys are cleaned up after the run.

S3-compatible profiles

s3
@aws-sdk/client-s3forcePathStyle

MinIO, Cloudflare R2, Backblaze B2, AWS, and other S3-compatible endpoints as source prefixes or destinations.

  • Custom endpoint + region + path-style for MinIO/R2/B2.
  • S3 sources only support sourceType=path (object prefix).
  • Encrypted artifacts and instance archives land as ordinary objects under a prefix.

age encryption vault

encryption
age_keysrecovery recipientsenableEncryption

Optional client-side age encryption before land. Bro destinations force encryption. Private identities stay on this instance.

  • Vault statuses: active (used for new encrypts), retired, compromised—decrypt tries all stored identities.
  • Create new key demotes the previous active to retired (never silent overwrite).
  • Every encrypt includes the active recipient plus all recovery recipients (offline age1… keys).
  • Passphrase-wrapped identity export for password managers; export acknowledgment checklist + Status nag when missing.

Bro Space

bro
destinationKind=peermailboxLazyBro/api/peers

1:1 reciprocal peer storage. Mailbox sync + LazyBro outbound agent; invite-code pairing. Encryption forced; offline sync is soft status.

  • Land stages age ciphertext locally; peer pulls via /api/peers/agent/* then acks (new pairs = mailbox).
  • LazyBro is outbound-only (mode=client, empty remote URL)—only your LazyBackup needs inbound reachability; LB↔LB both need URLs and both run the sync worker.
  • Invite + Accept (or paste in LazyBro); hard quotas; opaque blobs; Bro offline / sync pending does not fire failure webhooks.
  • Mailbox retention uses the same version-count and age/min-keep rules as S3. LazyBackup advertises deletes[]; LazyBro unlinks and acks. Open recalls are skipped until they finish.
  • Restore/download while the blob is still on the peer returns HTTP 202 (waiting)—keep LazyBro running so the recall can finish. Not a backup failure.
  • Download LazyBro: https://github.com/Ceneka/lazybackup/releases/tag/lazybro (linux-x64, linux-arm64, darwin-arm64, darwin-x64, windows-x64).
  • Optional Tailscale on the LazyBackup host (not bundled in the image) so friends can dial a 100.x address.

Instance meta-backup

instance-backup
lazybackup_instanceinstanceBackupPassphrase

Back up LazyBackup itself: SQLite database, age vault secrets, and SSH key rows—schedulable like any other job.

  • Settings → Encryption links to New Backup prefilled with source=lazybackup_instance.
  • Optional passphrase wraps the tarball with age setPassphrase (avoids encrypting the vault with a key stored inside it).
  • Cannot use Bro destinations. Restore is manual (replace DB / import keys)—no in-place auto-restore.

Status posture

status
GET /api/status/status

Operator checklist derived from stored config—not a separate monitoring agent.

  • Critical / warn / info / ok checks for unlocked UI, missing age key, unexported keys, no recovery recipients, missing or stale instance backup, missed (overdue) schedules, password-only SSH servers, recent failures, webhooks, remote_exec tokens.
  • Each row deep-links to Settings, Backups, or History.
  • Refreshes about every minute from a single session-authenticated API.

Restore

restore
POST /api/history/:id/restoreGET /api/history/:id/download

Path, volume, and database restore from History. Artifacts can be local, on S3, on Bro, or on an SSH destination with key auth (pulled onto this host first).

  • Path: rsync/push the backed-up tree back to the local path, SSH host, or S3 source prefix. History offers a host picker to restore onto a different server (key auth required).
  • Docker volume: push/extract into a named volume on the original source or a retargeted host (SSH server or this host’s Docker socket).
  • Database: pipe into psql/mysql/sqlite (native or docker exec) on the original or a retargeted host.
  • .age archives decrypt automatically. Download the artifact from History without restoring in place. Password-only SSH destinations cannot pull for restore or download.

Servers & SSH keys

servers
node-sshauthType password|key

Manage VPS connections—host, port, user, credentials—then reuse them as From or To.

  • Password auth works for Test connection and helpers (list volumes/containers, remote shell).
  • Path transfers require an SSH key on every server endpoint involved.
  • Store keys in the app, reference a system path, or attach a saved ssh_keys row.

Jobs, schedule, retention

jobs
cronenableVersioningenableFileRetention

Cron in the app timezone, excludes, pre-backup commands, versioned subfolders, and age-based file retention.

  • 5-field cron; invalid expressions fail at schedule time. New Backup offers preset chips and VPS path / volume recipes for a first job.
  • Versioning: YYYY-MM-DD_HH-mm-ss subfolders with count-based keep.
  • File retention: age + min-keep for dump-style destinations (not with versioning)—local, remote SSH, S3, and Bro mailbox.
  • Pre-backup commands: SSH or local shell; Bearer tokens need remote_exec to set/change them.

Validate before run

validate
POST /api/backups/:id/validatelastValidation

Dry-run connectivity and credentials without transferring data. Open a backup → Validate; the last result is stored for later.

  • Probes SSH reachability / key capability, local or remote paths, S3 profile access, and database dump credentials as applicable.
  • Returns pass / fail / warn checks with messages—no rsync, pack, or dump side effects.
  • lastValidatedAt / lastValidationOk / lastValidationChecks persist on the config; cleared when you edit the job.

Failure webhooks

webhooks
failureWebhookUrlsuccessPingUrlbackup.failed

Optional HTTPS notification when a backup fails, plus optional success pings. Configure once under Settings → General; empty URL disables.

  • Method GET / POST / PUT; URL, headers, and body support {{event}}, {{backupName}}, {{configId}}, {{historyId}}, {{errorMessage}}, {{endedAt}}.
  • Failure presets: Default JSON, Discord, Telegram, Uptime Kuma push, ntfy, Slack—paste your URL and tweak the template.
  • Success pings: Healthchecks-style GET (or POST) on backup.succeeded; Discord, ntfy, and Telegram presets share the same channel templates.
  • Empty POST/PUT body sends the built-in backup.failed JSON. HTTPS required (http only for localhost/LAN). Send test notification to verify.

App password & passkeys

auth
lb_sessionWebAuthnwebauthn_credentials

Single-operator lock—no users table. Auth enables when a password hash or ≥1 passkey exists.

  • App password: argon2 hash in settings; set/skip on first run.
  • Passkeys: register in Settings; Sign in with passkey on /login.
  • Session cookie lb_session, 30-day sliding expiry. AUTH_COOKIE_SECURE=true only behind HTTPS.
  • Middleware checks the session in-process on the Node.js runtime (no HTTP self-fetch).

MCP & API tokens

mcp
/mcpAuthorization: Bearerremote_execread_only

Streamable HTTP MCP on the same instance so coding agents manage backups without a local bridge.

  • Create tokens under Settings → API / MCP (shown once; hashed at rest; revoke anytime).
  • Tokens cannot mint tokens—CRUD requires a browser session.
  • validate_backup probes endpoints without transferring (persists lastValidation). get_status returns the Status posture snapshot. Both are allowed for read_only tokens.
  • remote_exec gates exec_command and changing preBackupCommands; sessions always allowed. read_only (mutually exclusive with remote_exec) may list/get, validate, and test_*; it cannot mutate. Existing tokens without it still write.
  • Destructive tools (delete_*, restore_history, exec_command) require confirm=true. Audit log without secrets.

Deploy

Docker image ghcr.io/ceneka/lazybackup:latest (linux/amd64 and linux/arm64). Unraid and TrueNAS SCALE templates are in deploy/.