Skip to content

incus: new integration - backup and restore Incus containers#62

Open
pata27 wants to merge 22 commits into
PlakarKorp:mainfrom
pata27:integration/incus
Open

incus: new integration - backup and restore Incus containers#62
pata27 wants to merge 22 commits into
PlakarKorp:mainfrom
pata27:integration/incus

Conversation

@pata27

@pata27 pata27 commented Jul 10, 2026

Copy link
Copy Markdown

New integration for Incus: back up containers as browsable file-level snapshots and restore them as new instances, through the Incus backup API. Everything lives under incus/.

What it does

  • Importer: creates a temporary server-side backup (InstanceOnly, no compression so dedup works), streams the tarball and replays it as file records, then deletes the backup (TTL as a safety net). Refuses VMs, warns on stderr about attached volumes the backup does not contain.
  • Exporter: rebuilds the tarball from records and hands it to CreateInstanceFromBackup, with an existing-name pre-check before any upload.

Fidelity

Hardlinks (true hardlinks incus-to-incus, portable relative symlinks elsewhere), setuid/setgid/sticky, device major/minor, fifos, uid/gid/mtime. Extended attributes and file capabilities are captured as kloset xattr records and reinjected on export; restoring them end to end needs kloset's Snapshot.Export() to forward xattr records to exporters, which it currently doesn't.

Options

socket or url + TLS client certificates (remote daemons, self-signed pinning or PKI), project (with project-qualified snapshot origins), pool, target (cluster member placement on restore), backup_ttl / cleanup_timeout.

Validation

  • Unit tests (-race) on both connectors and the connection layer; golangci-lint clean.
  • Live e2e roundtrip (make test-integration) on a 3-member Incus 6.x cluster: real Alpine container, fixtures for setuid/hardlink/symlink/fifo, full rootfs manifest comparison after restore.
  • plakar pkg create checked against plakar v1.1.4.

exporter/header.go masked the tar Mode field with fs.ModePerm (0o777),
dropping any setuid/setgid/sticky bit before restore (e.g. a 04755
sudo binary came back as 0755). Mask with 0o7777 instead to keep the
special bits, symmetric with the importer's raw-bit-preserving cast
in finfo.go. Added round-trip tests in both directions.

Also: note device node major/minor loss in README caveats, document
the tar record-ordering assumption in the exporter write loop, and
add the ISC header to the plugin entrypoints.
The importer now replays each tar entry's PAX SCHILY.xattr.* records as
kloset xattr Records right after the owning file record (mirroring the
reference fs importer's file-then-xattr order, which this module's
exporter already relies on for record replay ordering).

The exporter buffers one record at a time so a file record's xattr(s),
arriving right after it, can be folded into its rebuilt tar header's
PAXRecords before the header is written - fixing security.capability
(e.g. ping's cap_net_raw) and other xattrs silently dropping on restore.
Validated on a 3-member cluster; instances are pinned to the local
member because cross-node exec output arrives truncated over the
local unix socket.
@omar-polo

Copy link
Copy Markdown
Collaborator

Thank you very much!

During the week I'll do a proper review and give you feedbacks (if any!)

Just a parethesis though, I realized we still need to clarify it in the README, but the integrations repository is only for the integrations developed by ourselves. For third-party integrations (which, don't get me wrong, we highly appreciate!) the suggestion is to let the author host it (any git repository is fine, doesn't even need to be on github!) and then open a PR to hub.git with the recipe for inclusion, so that it gets packaged and distributed automatically.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants