{
  "schema_version": 3,
  "app": "backup-restic",
  "display_name": "restic backup benchmark",
  "upstream_repo": "https://github.com/restic/restic",
  "docs_url": "https://restic.readthedocs.io/en/stable/",
  "status": "ok",
  "summary": {
    "primary_service": "restic",
    "app_version": "0.19.1",
    "app_version_pinned": true,
    "runs": 3,
    "publishable": true,
    "publishable_note": null,
    "idle_total_mem_mib": 0.59,
    "idle_total_cpu_pct": 0.41,
    "peak_total_mem_mib": 110.9,
    "peak_is_under_load": true,
    "startup_seconds": 5.4,
    "startup_peak_total_mem_mib": 0.84,
    "settled": true,
    "settle_seconds_used": 121.1,
    "peak_max_mib": 115.5,
    "sizing_peak_mib": 115.5,
    "idle_mem_mib": {
      "median": 0.59,
      "min": 0.51,
      "max": 0.6,
      "values": [
        0.51,
        0.6,
        0.59
      ]
    },
    "idle_cpu_pct": {
      "median": 0.41,
      "min": 0.38,
      "max": 0.41,
      "values": [
        0.41,
        0.38,
        0.41
      ]
    },
    "peak_mem_mib": {
      "median": 110.9,
      "min": 106.7,
      "max": 115.5,
      "values": [
        106.7,
        115.5,
        110.9
      ]
    },
    "startup_peak_mem_mib": {
      "median": 0.84,
      "min": 0.77,
      "max": 0.85,
      "values": [
        0.85,
        0.77,
        0.84
      ]
    },
    "startup_s": {
      "median": 5.4,
      "min": 5.4,
      "max": 5.4,
      "values": [
        5.4,
        5.4,
        5.4
      ]
    },
    "settled_after_healthy_s": {
      "median": 128.7,
      "min": 128.7,
      "max": 130.7,
      "values": [
        130.7,
        128.7,
        128.7
      ]
    },
    "settled_runs": 3,
    "per_service": {
      "restic": {
        "idle_mem_mib": {
          "median": 0.59,
          "min": 0.51,
          "max": 0.6,
          "values": [
            0.51,
            0.6,
            0.59
          ]
        },
        "peak_mem_mib": {
          "median": 110.9,
          "min": 106.7,
          "max": 115.5,
          "values": [
            106.7,
            115.5,
            110.9
          ]
        }
      }
    },
    "variance_flag": null,
    "representative_run": 3,
    "cpu_models": [
      "INTEL(R) XEON(R) PLATINUM 8573C"
    ],
    "cpu_model_mismatch": false,
    "rule": "idle = median of the run idle means; peak = median of the run peaks (highest simultaneous total, under load when the app has a load step); peak_max_mib = highest run peak; sizing_peak_mib = highest over runs of max(run peak, run startup peak); variance_flag when max/min over runs is above 1.5 for idle or peak."
  },
  "run": {
    "started_at": "2026-10-19T03:49:20Z",
    "date": "2026-10-19",
    "harness_version": "3.0.0",
    "runs_requested": 3,
    "docker_host": null,
    "remote": false,
    "runs_completed": 3
  },
  "host": {
    "label": "github-actions ubuntu-latest (2 vCPU, 8 GB)",
    "ci": {
      "runs_on": "ubuntu-latest",
      "runner_environment": "github-hosted",
      "runner_os": "Linux",
      "runner_arch": "X64",
      "image_os": "ubuntu24",
      "image_version": "20261012.334.1"
    },
    "cpu_model": "INTEL(R) XEON(R) PLATINUM 8573C",
    "cpu_model_note": null,
    "cores": 2,
    "mem_total_mib": 7937,
    "kernel": "6.17.0-1022-azure",
    "os": "Ubuntu 24.04.5 LTS",
    "arch": "x86_64",
    "oci_arch": "amd64",
    "docker_server_version": "28.0.4",
    "docker_client_version": "28.0.4",
    "compose_client_version": "2.38.2",
    "storage_driver": "overlay2",
    "containerd_image_store": false,
    "source": "docker info + throwaway container reading /proc/cpuinfo on the daemon host",
    "compose_note": "Compose runs on the client machine; Docker server fields are from the daemon host"
  },
  "helper_images": {
    "curlimages/curl:latest": {
      "pulled_as": "curlimages/curl:latest",
      "digest": "curlimages/curl@sha256:a134550be58ab783810e613ae96fd253c38f7bdd16d9bf9be350700c7a29ac06"
    },
    "python:3.12-slim": {
      "pulled_as": "python:3.12-slim",
      "digest": "python@sha256:a6e34c598f2467ed0e9a8d349809fcd8b5c603269512df273a0bb1784edc11b1"
    }
  },
  "pull_seconds": 2.3,
  "finished_at": "2026-10-19T03:59:48Z",
  "elapsed_seconds": 627.4,
  "services": {
    "restic": {
      "image_ref": "restic/restic:0.19.1",
      "image_repo": "restic/restic",
      "image_tag": "0.19.1",
      "digest": "sha256:136600b6ff6843d61d355f7f71f460a166429f35de6fd11b568fece3c9a4d510",
      "digest_note": null,
      "version": "0.19.1",
      "version_source": "exec",
      "version_pinned": true,
      "version_note": "version taken from the running app (prefer_exec), not from the image label",
      "resolved_tag": "0.19.1",
      "resolved_tag_note": null,
      "image_size": {
        "unpacked_mb": 45.5,
        "unpacked_source": "docker image ls Size: unpacked image on the daemon host, rounded by docker to 3 significant figures",
        "compressed_mb": 17.5,
        "compressed_source": "sum of compressed layer sizes in the registry manifest for linux/amd64",
        "compressed_null_reason": null,
        "unit_note": "MB = 10^6 bytes. unpacked = disk used on the host; compressed = what is downloaded"
      },
      "compose_image": "restic/restic:0.19.1",
      "compose_comment": "sha256:136600b6ff6843d61d355f7f71f460a166429f35de6fd11b568fece3c9a4d510  (version 0.19.1, from exec)",
      "restart_count": 0,
      "oom_killed": false
    }
  },
  "runs": [
    {
      "run": 1,
      "started_at": "2026-10-19T03:49:29Z",
      "host": {
        "label": "github-actions ubuntu-latest (2 vCPU, 8 GB)",
        "ci": {
          "runs_on": "ubuntu-latest",
          "runner_environment": "github-hosted",
          "runner_os": "Linux",
          "runner_arch": "X64",
          "image_os": "ubuntu24",
          "image_version": "20261012.334.1"
        },
        "cpu_model": "INTEL(R) XEON(R) PLATINUM 8573C",
        "cpu_model_note": null,
        "cores": 2,
        "mem_total_mib": 7937,
        "kernel": "6.17.0-1022-azure",
        "os": "Ubuntu 24.04.5 LTS",
        "arch": "x86_64",
        "oci_arch": "amd64",
        "docker_server_version": "28.0.4",
        "docker_client_version": "28.0.4",
        "compose_client_version": "2.38.2",
        "storage_driver": "overlay2",
        "containerd_image_store": false,
        "source": "docker info + throwaway container reading /proc/cpuinfo on the daemon host",
        "compose_note": "Compose runs on the client machine; Docker server fields are from the daemon host"
      },
      "date": "2026-10-19",
      "startup": {
        "seconds": 5.4,
        "ok": true,
        "checks": [
          {
            "type": "container",
            "ok": true,
            "ready_after_s": 5.4,
            "detail": "shb-backup-restic-restic-1=running/healthy"
          }
        ],
        "up_command_seconds": 0.2,
        "definition": "wall clock from starting `docker compose up -d` until every health check passed; checks poll every 0.5 s from a throwaway container on the compose network, so resolution is about +/- 1 s"
      },
      "versions": {
        "restic": "0.19.1"
      },
      "setup": [
        {
          "exit_code": 0,
          "duration_s": 8.0,
          "reported": {
            "dataset": {
              "day1": {
                "files": 3348,
                "bytes": 334222462,
                "unique_file_bytes": 265791580,
                "duplicate_file_bytes": 68430882,
                "symlinks": 2,
                "docs_files": 3000,
                "docs_bytes": 21333065,
                "docs_zlib6_ratio": 3.15
              },
              "day2": {
                "files": 3369,
                "bytes": 342525272,
                "unique_file_bytes": 275936490,
                "duplicate_file_bytes": 66588782,
                "modified_files": 167,
                "added_files": 41,
                "deleted_files": 20,
                "modified_pct_of_day1_files": 4.99,
                "changed_bytes_whole_files": 64380805,
                "delta_bytes_new_content": 13838607
              },
              "seed": 20261019,
              "mtime_day1": 1790856000,
              "mtime_day2": 1790942400
            }
          },
          "output_tail": [],
          "description": "Generate the benchmark dataset with gen_dataset.py (standard library, deterministic, no downloads) into the tool's /data volume: /data/src with 3,348 files, about 334 MB (the 103 random-byte files of the file-sync tests, about 194.5 MB; 3,000 compressible text files of 0.5-24 KB; 41 exact duplicates including one of a 50 MB file; 200 identical template files; one 50 MB near-duplicate with four 4 KB regions changed; three oddly named files; two symlinks), plus the 'day 2' change set (about 5 % of the files modified, 41 added, 20 deleted) staged outside /data/src and the SHA-256 manifests of both days. Not measured: it runs before the idle window. The tool never sees the staging area."
        }
      ],
      "idle": {
        "per_service": {
          "restic": {
            "samples": 16,
            "mem_mean_mib": 0.51,
            "mem_max_mib": 0.72,
            "cpu_mean_pct": 0.41,
            "cpu_max_pct": 2.23
          }
        },
        "total": {
          "samples": 16,
          "dropped_for_total": 0,
          "mem_mean_mib": 0.51,
          "mem_max_mib": 0.72,
          "cpu_mean_pct": 0.41,
          "cpu_max_pct": 2.23
        },
        "window_seconds": 30.3,
        "series": [
          {
            "t_offset_s": 0.0,
            "total_mem_mib": 0.35,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 2.0,
            "total_mem_mib": 0.49,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 4.0,
            "total_mem_mib": 0.49,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 6.1,
            "total_mem_mib": 0.72,
            "total_cpu_pct": 2.13
          },
          {
            "t_offset_s": 8.1,
            "total_mem_mib": 0.49,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 10.1,
            "total_mem_mib": 0.49,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 12.1,
            "total_mem_mib": 0.38,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 14.1,
            "total_mem_mib": 0.38,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 16.2,
            "total_mem_mib": 0.59,
            "total_cpu_pct": 2.17
          },
          {
            "t_offset_s": 18.2,
            "total_mem_mib": 0.49,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 20.2,
            "total_mem_mib": 0.49,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 22.2,
            "total_mem_mib": 0.7,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 24.2,
            "total_mem_mib": 0.7,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 26.2,
            "total_mem_mib": 0.7,
            "total_cpu_pct": 2.23
          },
          {
            "t_offset_s": 28.3,
            "total_mem_mib": 0.35,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 30.3,
            "total_mem_mib": 0.35,
            "total_cpu_pct": 0.0
          }
        ],
        "memory_stat": {
          "read_at": "2026-10-19T03:52:15Z",
          "per_service": {
            "restic": {
              "cgroup": "v2",
              "anon_mib": 0.09,
              "file_mib": 0.0,
              "active_file_mib": 0.0,
              "inactive_file_mib": 0.0,
              "shmem_mib": 0.0
            }
          },
          "read_lag_s": 0.0,
          "method": "docker exec <container> cat /sys/fs/cgroup/memory.stat (cgroup v1: /sys/fs/cgroup/memory/memory.stat); MiB; splits what docker stats shows as one number into anon (heap) and file cache; a field that is absent was not printed by the kernel or not readable (see reason)",
          "taken": "right after the idle window"
        }
      },
      "settle": {
        "settled": true,
        "mode": "detected",
        "settle_seconds_used": 121.1,
        "settle_started_after_healthy_s": 9.6,
        "params": {
          "window_seconds": 60,
          "threshold_pct": 2.0,
          "threshold_min_mib": 10.0,
          "min_seconds": 120,
          "max_seconds": 600
        },
        "last_window": {
          "n": 30,
          "mean_mib": 0.64,
          "min_mib": 0.35,
          "max_mib": 0.85,
          "spread_mib": 0.5,
          "threshold_mib": 10.0
        },
        "rechecks": 0,
        "rule": "settled = over the last window_seconds the stack total varied (max - min) by less than max(threshold_pct of the window mean, threshold_min_mib); not before min_seconds, at the latest max_seconds, both counted from the end of the setup steps; the idle window must also be flat and agree with it. If max_seconds passes without settling, settled=false and the idle window is measured anyway."
      },
      "startup_peak": {
        "per_service_mem_mib": {
          "restic": 0.85
        },
        "total_mem_mib": 0.85,
        "samples": 65,
        "note": "highest value across docker stats samples (about one every 2-3 s) from healthy until the stack settled; shorter spikes between samples are not seen"
      },
      "timeline": {
        "step_s": 10,
        "t0": "healthy",
        "offsets_s": [
          10,
          20,
          30,
          40,
          50,
          60,
          70,
          80,
          90,
          100,
          110,
          120,
          130,
          140,
          150,
          160,
          170
        ],
        "total_mem_mib": [
          0.5,
          0.5,
          0.5,
          0.6,
          0.7,
          0.6,
          0.6,
          0.7,
          0.7,
          0.6,
          0.7,
          0.6,
          0.6,
          0.5,
          0.5,
          0.6,
          0.4
        ],
        "per_service_mem_mib": {
          "restic": [
            0.5,
            0.5,
            0.5,
            0.6,
            0.7,
            0.6,
            0.6,
            0.7,
            0.7,
            0.6,
            0.7,
            0.6,
            0.6,
            0.5,
            0.5,
            0.6,
            0.4
          ]
        }
      },
      "load": {
        "per_service": {
          "restic": {
            "samples": 13,
            "mem_mean_mib": 58.26,
            "mem_max_mib": 106.7,
            "cpu_mean_pct": 82.36,
            "cpu_max_pct": 125.38
          }
        },
        "total": {
          "samples": 13,
          "dropped_for_total": 0,
          "mem_mean_mib": 58.26,
          "mem_max_mib": 106.7,
          "cpu_mean_pct": 82.36,
          "cpu_max_pct": 125.38
        },
        "description": "The same fixed dataset for all three tools, run by one shell script inside the tool's own container (docker exec), so the backup is in the cgroup that docker stats measures. Steps: check /data/src against the day-1 SHA-256 manifest; create an encrypted local repository on a Docker volume (init); first backup of the 334 MB; the same backup into a second repository with compression off (restic and Borg; Kopia's default is already off); apply the day-2 change set (about 5 % of the files modified, 41 added, 20 deleted) and check the source against the day-2 manifest; second backup (incremental); a third backup with nothing changed; each tool's default integrity check and its deepest one (restic check [--read-data], borg check [--verify-data], kopia snapshot verify [--verify-files-percent=100]); restore the latest snapshot into an empty directory and compare the SHA-256 of every restored file and every symlink with the manifest. Each timed step is sampled every 0.1 s for the peak resident memory (VmHWM) and CPU time of the tool's processes. Local repository only: no network, cloud, S3, B2 or SFTP.",
        "duration_s": 22.3,
        "window": "from LAB_MARK load_start (or step start) until 2 s after the step ended",
        "reported_by_script": {
          "tool": "restic",
          "version": "0.19.1",
          "cli_startup_seconds": 0.01,
          "nproc": 2,
          "cpu_model": "INTEL(R) XEON(R) PLATINUM 8573C",
          "cpu_aes": true,
          "cpu_sha_ni": true,
          "cpu_avx2": true,
          "init": {
            "seconds": 2.54,
            "ok": true,
            "exit_code": 0,
            "repo_bytes": 602,
            "repo_disk_bytes": 1081344,
            "repo_files": 2,
            "peak_rss_mib": 91.7,
            "cpu_seconds": 2.42
          },
          "settings": "{\"version\":2,\"id\":\"110f2f2c303c29ace665bee7af9c17be0d12824669f5b93e3699fd113f284fd6\",\"chunker_polynomial\":\"32b33dcd22cb09\"}",
          "first_backup": {
            "seconds": 3.38,
            "ok": true,
            "exit_code": 0,
            "repo_bytes": 210174967,
            "repo_disk_bytes": 211292160,
            "repo_files": 17,
            "peak_rss_mib": 117.5,
            "cpu_seconds": 3.08,
            "files": 3348,
            "source_bytes": 334222462
          },
          "first_backup_no_compression": {
            "seconds": 3.27,
            "ok": true,
            "exit_code": 0,
            "repo_bytes": 226087510,
            "repo_disk_bytes": 227205120,
            "repo_files": 18,
            "peak_rss_mib": 105.9,
            "cpu_seconds": 2.37,
            "files": 3348,
            "source_bytes": 334222462,
            "compression": "off"
          },
          "incremental": {
            "seconds": 1.25,
            "ok": true,
            "exit_code": 0,
            "repo_bytes": 229376141,
            "repo_disk_bytes": 230510592,
            "repo_files": 22,
            "peak_rss_mib": 97.2,
            "cpu_seconds": 0.9,
            "files": 3369,
            "source_bytes": 342525272,
            "changed_files": 208,
            "changed_bytes_whole_files": 64380805,
            "deleted_files": 20,
            "repo_growth_bytes": 19201174
          },
          "unchanged_rerun": {
            "seconds": 0.74,
            "ok": true,
            "exit_code": 0,
            "repo_bytes": 229376537,
            "repo_disk_bytes": 230514688,
            "repo_files": 23,
            "peak_rss_mib": 62.7,
            "cpu_seconds": 0.46,
            "files": 3369,
            "source_bytes": 342525272,
            "repo_growth_bytes": 396
          },
          "check_default": {
            "seconds": 0.73,
            "ok": true,
            "exit_code": 0,
            "peak_rss_mib": 62.7,
            "cpu_seconds": 0.47
          },
          "check_deep": {
            "seconds": 1.13,
            "ok": true,
            "exit_code": 0,
            "peak_rss_mib": 79.9,
            "cpu_seconds": 1.27
          },
          "restore": {
            "seconds": 1.26,
            "ok": true,
            "exit_code": 0,
            "files_verified": 3369,
            "files_expected": 3369,
            "files_restored": 3369,
            "missing_or_different": 0,
            "unexpected": 0,
            "sha256_match_all": true,
            "symlinks_match": true,
            "restored_bytes": 342525272,
            "peak_rss_mib": 140.0,
            "cpu_seconds": 1.32
          },
          "script_seconds": 22.25,
          "all_ok": true
        },
        "output_tail": [
          "source day 1: 3348 files, 334222462 bytes, matches manifest-day1 (3348 hashes)",
          "init: 2.54s",
          "first backup: 3.38s, repository 210174967 bytes, peak RSS 117.5 MiB",
          "first backup, compression off: 3.27s, repository 226087510 bytes",
          "source day 2: 3369 files, 342525272 bytes (208 files written or replaced, 20 deleted), matches manifest-day2",
          "incremental: 1.25s (first backup 3.38s), repository grew by 19201174 bytes, peak RSS 97.2 MiB",
          "unchanged re-run: 0.74s, repository grew by 396 bytes",
          "check: default {\"seconds\": 0.73, \"ok\": true, \"exit_code\": 0, \"peak_rss_mib\": 62.7, \"cpu_seconds\": 0.47}, deep {\"seconds\": 1.13, \"ok\": true, \"exit_code\": 0, \"peak_rss_mib\": 79.9, \"cpu_seconds\": 1.27}",
          "rm: can't remove '/restore': Resource busy",
          "restore: 1.26s, 3369 of 3369 files verified by sha256, symlinks match: true, peak RSS 140.0 MiB"
        ],
        "series": [
          {
            "t_offset_s": 0.0,
            "total_mem_mib": 2.18,
            "total_cpu_pct": 100.0
          },
          {
            "t_offset_s": 2.0,
            "total_mem_mib": 74.42,
            "total_cpu_pct": 105.7
          },
          {
            "t_offset_s": 4.0,
            "total_mem_mib": 44.15,
            "total_cpu_pct": 69.97
          },
          {
            "t_offset_s": 6.0,
            "total_mem_mib": 101.2,
            "total_cpu_pct": 82.61
          },
          {
            "t_offset_s": 8.0,
            "total_mem_mib": 82.52,
            "total_cpu_pct": 106.83
          },
          {
            "t_offset_s": 10.0,
            "total_mem_mib": 78.03,
            "total_cpu_pct": 93.25
          },
          {
            "t_offset_s": 12.0,
            "total_mem_mib": 106.7,
            "total_cpu_pct": 90.31
          },
          {
            "t_offset_s": 14.0,
            "total_mem_mib": 17.93,
            "total_cpu_pct": 103.02
          },
          {
            "t_offset_s": 16.0,
            "total_mem_mib": 60.1,
            "total_cpu_pct": 66.82
          },
          {
            "t_offset_s": 18.0,
            "total_mem_mib": 92.62,
            "total_cpu_pct": 125.38
          },
          {
            "t_offset_s": 20.1,
            "total_mem_mib": 33.42,
            "total_cpu_pct": 124.44
          },
          {
            "t_offset_s": 22.1,
            "total_mem_mib": 32.15,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 24.1,
            "total_mem_mib": 32.0,
            "total_cpu_pct": 2.32
          }
        ],
        "series_start_offset_s": 1.9,
        "peak_note": "peak = highest value seen across docker stats samples (about one every 2-3 s); shorter spikes between samples are not seen",
        "peak_memory_stat": {
          "read_at": "2026-10-19T03:52:29Z",
          "per_service": {
            "restic": {
              "cgroup": "v2",
              "anon_mib": 0.28,
              "file_mib": 422.14,
              "active_file_mib": 0.0,
              "inactive_file_mib": 422.14,
              "shmem_mib": 0.0
            }
          },
          "read_lag_s": 0.1,
          "series_index": 6,
          "sample_t_offset_s": 12.0,
          "total_mem_mib": 106.7
        }
      },
      "container_end_state": {
        "restic": {
          "restart_count": 0,
          "oom_killed": false
        }
      },
      "teardown": {
        "done": true,
        "volumes_removed": true,
        "images_removed": false,
        "error": null
      },
      "warnings": [],
      "finished_at": "2026-10-19T03:52:57Z",
      "elapsed_seconds": 208.4
    },
    {
      "run": 2,
      "started_at": "2026-10-19T03:52:57Z",
      "host": {
        "label": "github-actions ubuntu-latest (2 vCPU, 8 GB)",
        "ci": {
          "runs_on": "ubuntu-latest",
          "runner_environment": "github-hosted",
          "runner_os": "Linux",
          "runner_arch": "X64",
          "image_os": "ubuntu24",
          "image_version": "20261012.334.1"
        },
        "cpu_model": "INTEL(R) XEON(R) PLATINUM 8573C",
        "cpu_model_note": null,
        "cores": 2,
        "mem_total_mib": 7937,
        "kernel": "6.17.0-1022-azure",
        "os": "Ubuntu 24.04.5 LTS",
        "arch": "x86_64",
        "oci_arch": "amd64",
        "docker_server_version": "28.0.4",
        "docker_client_version": "28.0.4",
        "compose_client_version": "2.38.2",
        "storage_driver": "overlay2",
        "containerd_image_store": false,
        "source": "docker info + throwaway container reading /proc/cpuinfo on the daemon host",
        "compose_note": "Compose runs on the client machine; Docker server fields are from the daemon host"
      },
      "date": "2026-10-19",
      "startup": {
        "seconds": 5.4,
        "ok": true,
        "checks": [
          {
            "type": "container",
            "ok": true,
            "ready_after_s": 5.4,
            "detail": "shb-backup-restic-restic-1=running/healthy"
          }
        ],
        "up_command_seconds": 0.2,
        "definition": "wall clock from starting `docker compose up -d` until every health check passed; checks poll every 0.5 s from a throwaway container on the compose network, so resolution is about +/- 1 s"
      },
      "versions": {
        "restic": "0.19.1"
      },
      "setup": [
        {
          "exit_code": 0,
          "duration_s": 7.2,
          "reported": {
            "dataset": {
              "day1": {
                "files": 3348,
                "bytes": 334222462,
                "unique_file_bytes": 265791580,
                "duplicate_file_bytes": 68430882,
                "symlinks": 2,
                "docs_files": 3000,
                "docs_bytes": 21333065,
                "docs_zlib6_ratio": 3.15
              },
              "day2": {
                "files": 3369,
                "bytes": 342525272,
                "unique_file_bytes": 275936490,
                "duplicate_file_bytes": 66588782,
                "modified_files": 167,
                "added_files": 41,
                "deleted_files": 20,
                "modified_pct_of_day1_files": 4.99,
                "changed_bytes_whole_files": 64380805,
                "delta_bytes_new_content": 13838607
              },
              "seed": 20261019,
              "mtime_day1": 1790856000,
              "mtime_day2": 1790942400
            }
          },
          "output_tail": [],
          "description": "Generate the benchmark dataset with gen_dataset.py (standard library, deterministic, no downloads) into the tool's /data volume: /data/src with 3,348 files, about 334 MB (the 103 random-byte files of the file-sync tests, about 194.5 MB; 3,000 compressible text files of 0.5-24 KB; 41 exact duplicates including one of a 50 MB file; 200 identical template files; one 50 MB near-duplicate with four 4 KB regions changed; three oddly named files; two symlinks), plus the 'day 2' change set (about 5 % of the files modified, 41 added, 20 deleted) staged outside /data/src and the SHA-256 manifests of both days. Not measured: it runs before the idle window. The tool never sees the staging area."
        }
      ],
      "idle": {
        "per_service": {
          "restic": {
            "samples": 16,
            "mem_mean_mib": 0.6,
            "mem_max_mib": 0.75,
            "cpu_mean_pct": 0.38,
            "cpu_max_pct": 2.03
          }
        },
        "total": {
          "samples": 16,
          "dropped_for_total": 0,
          "mem_mean_mib": 0.6,
          "mem_max_mib": 0.75,
          "cpu_mean_pct": 0.38,
          "cpu_max_pct": 2.03
        },
        "window_seconds": 30.3,
        "series": [
          {
            "t_offset_s": 0.0,
            "total_mem_mib": 0.62,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 2.0,
            "total_mem_mib": 0.59,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 4.0,
            "total_mem_mib": 0.75,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 6.1,
            "total_mem_mib": 0.75,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 8.1,
            "total_mem_mib": 0.66,
            "total_cpu_pct": 1.98
          },
          {
            "t_offset_s": 10.1,
            "total_mem_mib": 0.66,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 12.1,
            "total_mem_mib": 0.59,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 14.1,
            "total_mem_mib": 0.5,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 16.1,
            "total_mem_mib": 0.5,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 18.2,
            "total_mem_mib": 0.66,
            "total_cpu_pct": 2.03
          },
          {
            "t_offset_s": 20.2,
            "total_mem_mib": 0.66,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 22.2,
            "total_mem_mib": 0.66,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 24.2,
            "total_mem_mib": 0.57,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 26.2,
            "total_mem_mib": 0.57,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 28.3,
            "total_mem_mib": 0.45,
            "total_cpu_pct": 2.01
          },
          {
            "t_offset_s": 30.3,
            "total_mem_mib": 0.45,
            "total_cpu_pct": 0.0
          }
        ],
        "memory_stat": {
          "read_at": "2026-10-19T03:55:42Z",
          "per_service": {
            "restic": {
              "cgroup": "v2",
              "anon_mib": 0.09,
              "file_mib": 0.0,
              "active_file_mib": 0.0,
              "inactive_file_mib": 0.0,
              "shmem_mib": 0.0
            }
          },
          "read_lag_s": 0.0,
          "method": "docker exec <container> cat /sys/fs/cgroup/memory.stat (cgroup v1: /sys/fs/cgroup/memory/memory.stat); MiB; splits what docker stats shows as one number into anon (heap) and file cache; a field that is absent was not printed by the kernel or not readable (see reason)",
          "taken": "right after the idle window"
        }
      },
      "settle": {
        "settled": true,
        "mode": "detected",
        "settle_seconds_used": 121.1,
        "settle_started_after_healthy_s": 7.6,
        "params": {
          "window_seconds": 60,
          "threshold_pct": 2.0,
          "threshold_min_mib": 10.0,
          "min_seconds": 120,
          "max_seconds": 600
        },
        "last_window": {
          "n": 30,
          "mean_mib": 0.56,
          "min_mib": 0.34,
          "max_mib": 0.77,
          "spread_mib": 0.43,
          "threshold_mib": 10.0
        },
        "rechecks": 0,
        "rule": "settled = over the last window_seconds the stack total varied (max - min) by less than max(threshold_pct of the window mean, threshold_min_mib); not before min_seconds, at the latest max_seconds, both counted from the end of the setup steps; the idle window must also be flat and agree with it. If max_seconds passes without settling, settled=false and the idle window is measured anyway."
      },
      "startup_peak": {
        "per_service_mem_mib": {
          "restic": 0.77
        },
        "total_mem_mib": 0.77,
        "samples": 64,
        "note": "highest value across docker stats samples (about one every 2-3 s) from healthy until the stack settled; shorter spikes between samples are not seen"
      },
      "timeline": {
        "step_s": 10,
        "t0": "healthy",
        "offsets_s": [
          10,
          20,
          30,
          40,
          50,
          60,
          70,
          80,
          90,
          100,
          110,
          120,
          130,
          140,
          150,
          160
        ],
        "total_mem_mib": [
          0.4,
          0.6,
          0.6,
          0.5,
          0.6,
          0.6,
          0.5,
          0.7,
          0.6,
          0.6,
          0.5,
          0.5,
          0.5,
          0.7,
          0.6,
          0.5
        ],
        "per_service_mem_mib": {
          "restic": [
            0.4,
            0.6,
            0.6,
            0.5,
            0.6,
            0.6,
            0.5,
            0.7,
            0.6,
            0.6,
            0.5,
            0.5,
            0.5,
            0.7,
            0.6,
            0.5
          ]
        }
      },
      "load": {
        "per_service": {
          "restic": {
            "samples": 13,
            "mem_mean_mib": 57.87,
            "mem_max_mib": 115.5,
            "cpu_mean_pct": 86.26,
            "cpu_max_pct": 126.51
          }
        },
        "total": {
          "samples": 13,
          "dropped_for_total": 0,
          "mem_mean_mib": 57.87,
          "mem_max_mib": 115.5,
          "cpu_mean_pct": 86.26,
          "cpu_max_pct": 126.51
        },
        "description": "The same fixed dataset for all three tools, run by one shell script inside the tool's own container (docker exec), so the backup is in the cgroup that docker stats measures. Steps: check /data/src against the day-1 SHA-256 manifest; create an encrypted local repository on a Docker volume (init); first backup of the 334 MB; the same backup into a second repository with compression off (restic and Borg; Kopia's default is already off); apply the day-2 change set (about 5 % of the files modified, 41 added, 20 deleted) and check the source against the day-2 manifest; second backup (incremental); a third backup with nothing changed; each tool's default integrity check and its deepest one (restic check [--read-data], borg check [--verify-data], kopia snapshot verify [--verify-files-percent=100]); restore the latest snapshot into an empty directory and compare the SHA-256 of every restored file and every symlink with the manifest. Each timed step is sampled every 0.1 s for the peak resident memory (VmHWM) and CPU time of the tool's processes. Local repository only: no network, cloud, S3, B2 or SFTP.",
        "duration_s": 22.3,
        "window": "from LAB_MARK load_start (or step start) until 2 s after the step ended",
        "reported_by_script": {
          "tool": "restic",
          "version": "0.19.1",
          "cli_startup_seconds": 0.01,
          "nproc": 2,
          "cpu_model": "INTEL(R) XEON(R) PLATINUM 8573C",
          "cpu_aes": true,
          "cpu_sha_ni": true,
          "cpu_avx2": true,
          "init": {
            "seconds": 2.45,
            "ok": true,
            "exit_code": 0,
            "repo_bytes": 603,
            "repo_disk_bytes": 1081344,
            "repo_files": 2,
            "peak_rss_mib": 91.7,
            "cpu_seconds": 2.4
          },
          "settings": "{\"version\":2,\"id\":\"0582363332bcb45741eb3df71c59784bceda7eb7c7b6e401141354e0a2f9e3c4\",\"chunker_polynomial\":\"3747e9fc33dbb9\"}",
          "first_backup": {
            "seconds": 3.13,
            "ok": true,
            "exit_code": 0,
            "repo_bytes": 210618936,
            "repo_disk_bytes": 211734528,
            "repo_files": 18,
            "peak_rss_mib": 121.1,
            "cpu_seconds": 3.02,
            "files": 3348,
            "source_bytes": 334222462
          },
          "first_backup_no_compression": {
            "seconds": 3.19,
            "ok": true,
            "exit_code": 0,
            "repo_bytes": 224390958,
            "repo_disk_bytes": 225505280,
            "repo_files": 18,
            "peak_rss_mib": 109.6,
            "cpu_seconds": 2.48,
            "files": 3348,
            "source_bytes": 334222462,
            "compression": "off"
          },
          "incremental": {
            "seconds": 1.05,
            "ok": true,
            "exit_code": 0,
            "repo_bytes": 226884847,
            "repo_disk_bytes": 228012032,
            "repo_files": 22,
            "peak_rss_mib": 111.4,
            "cpu_seconds": 0.86,
            "files": 3369,
            "source_bytes": 342525272,
            "changed_files": 208,
            "changed_bytes_whole_files": 64380805,
            "deleted_files": 20,
            "repo_growth_bytes": 16265911
          },
          "unchanged_rerun": {
            "seconds": 0.74,
            "ok": true,
            "exit_code": 0,
            "repo_bytes": 226885246,
            "repo_disk_bytes": 228016128,
            "repo_files": 23,
            "peak_rss_mib": 62.7,
            "cpu_seconds": 0.43,
            "files": 3369,
            "source_bytes": 342525272,
            "repo_growth_bytes": 399
          },
          "check_default": {
            "seconds": 0.72,
            "ok": true,
            "exit_code": 0,
            "peak_rss_mib": 62.6,
            "cpu_seconds": 0.43
          },
          "check_deep": {
            "seconds": 1.15,
            "ok": true,
            "exit_code": 0,
            "peak_rss_mib": 83.6,
            "cpu_seconds": 1.26
          },
          "restore": {
            "seconds": 1.18,
            "ok": true,
            "exit_code": 0,
            "files_verified": 3369,
            "files_expected": 3369,
            "files_restored": 3369,
            "missing_or_different": 0,
            "unexpected": 0,
            "sha256_match_all": true,
            "symlinks_match": true,
            "restored_bytes": 342525272,
            "peak_rss_mib": 133.0,
            "cpu_seconds": 1.23
          },
          "script_seconds": 22.32,
          "all_ok": true
        },
        "output_tail": [
          "source day 1: 3348 files, 334222462 bytes, matches manifest-day1 (3348 hashes)",
          "init: 2.45s",
          "first backup: 3.13s, repository 210618936 bytes, peak RSS 121.1 MiB",
          "first backup, compression off: 3.19s, repository 224390958 bytes",
          "source day 2: 3369 files, 342525272 bytes (208 files written or replaced, 20 deleted), matches manifest-day2",
          "incremental: 1.05s (first backup 3.13s), repository grew by 16265911 bytes, peak RSS 111.4 MiB",
          "unchanged re-run: 0.74s, repository grew by 399 bytes",
          "check: default {\"seconds\": 0.72, \"ok\": true, \"exit_code\": 0, \"peak_rss_mib\": 62.6, \"cpu_seconds\": 0.43}, deep {\"seconds\": 1.15, \"ok\": true, \"exit_code\": 0, \"peak_rss_mib\": 83.6, \"cpu_seconds\": 1.26}",
          "rm: can't remove '/restore': Resource busy",
          "restore: 1.18s, 3369 of 3369 files verified by sha256, symlinks match: true, peak RSS 133.0 MiB"
        ],
        "series": [
          {
            "t_offset_s": 0.0,
            "total_mem_mib": 2.03,
            "total_cpu_pct": 101.13
          },
          {
            "t_offset_s": 2.0,
            "total_mem_mib": 72.38,
            "total_cpu_pct": 105.54
          },
          {
            "t_offset_s": 4.0,
            "total_mem_mib": 80.8,
            "total_cpu_pct": 71.15
          },
          {
            "t_offset_s": 6.0,
            "total_mem_mib": 97.11,
            "total_cpu_pct": 122.12
          },
          {
            "t_offset_s": 8.0,
            "total_mem_mib": 80.8,
            "total_cpu_pct": 106.57
          },
          {
            "t_offset_s": 10.0,
            "total_mem_mib": 48.68,
            "total_cpu_pct": 106.67
          },
          {
            "t_offset_s": 12.0,
            "total_mem_mib": 115.5,
            "total_cpu_pct": 70.86
          },
          {
            "t_offset_s": 14.0,
            "total_mem_mib": 17.84,
            "total_cpu_pct": 100.55
          },
          {
            "t_offset_s": 16.0,
            "total_mem_mib": 59.87,
            "total_cpu_pct": 97.05
          },
          {
            "t_offset_s": 18.0,
            "total_mem_mib": 80.24,
            "total_cpu_pct": 126.51
          },
          {
            "t_offset_s": 20.0,
            "total_mem_mib": 33.29,
            "total_cpu_pct": 113.26
          },
          {
            "t_offset_s": 22.1,
            "total_mem_mib": 31.93,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 24.1,
            "total_mem_mib": 31.9,
            "total_cpu_pct": 0.0
          }
        ],
        "series_start_offset_s": 1.9,
        "peak_note": "peak = highest value seen across docker stats samples (about one every 2-3 s); shorter spikes between samples are not seen",
        "peak_memory_stat": {
          "read_at": "2026-10-19T03:55:56Z",
          "per_service": {
            "restic": {
              "cgroup": "v2",
              "anon_mib": 85.41,
              "file_mib": 448.54,
              "active_file_mib": 0.0,
              "inactive_file_mib": 448.54,
              "shmem_mib": 0.0
            }
          },
          "read_lag_s": 0.0,
          "series_index": 6,
          "sample_t_offset_s": 12.0,
          "total_mem_mib": 115.5
        }
      },
      "container_end_state": {
        "restic": {
          "restart_count": 0,
          "oom_killed": false
        }
      },
      "teardown": {
        "done": true,
        "volumes_removed": true,
        "images_removed": false,
        "error": null
      },
      "warnings": [],
      "finished_at": "2026-10-19T03:56:24Z",
      "elapsed_seconds": 206.3
    },
    {
      "run": 3,
      "started_at": "2026-10-19T03:56:24Z",
      "host": {
        "label": "github-actions ubuntu-latest (2 vCPU, 8 GB)",
        "ci": {
          "runs_on": "ubuntu-latest",
          "runner_environment": "github-hosted",
          "runner_os": "Linux",
          "runner_arch": "X64",
          "image_os": "ubuntu24",
          "image_version": "20261012.334.1"
        },
        "cpu_model": "INTEL(R) XEON(R) PLATINUM 8573C",
        "cpu_model_note": null,
        "cores": 2,
        "mem_total_mib": 7937,
        "kernel": "6.17.0-1022-azure",
        "os": "Ubuntu 24.04.5 LTS",
        "arch": "x86_64",
        "oci_arch": "amd64",
        "docker_server_version": "28.0.4",
        "docker_client_version": "28.0.4",
        "compose_client_version": "2.38.2",
        "storage_driver": "overlay2",
        "containerd_image_store": false,
        "source": "docker info + throwaway container reading /proc/cpuinfo on the daemon host",
        "compose_note": "Compose runs on the client machine; Docker server fields are from the daemon host"
      },
      "date": "2026-10-19",
      "startup": {
        "seconds": 5.4,
        "ok": true,
        "checks": [
          {
            "type": "container",
            "ok": true,
            "ready_after_s": 5.4,
            "detail": "shb-backup-restic-restic-1=running/healthy"
          }
        ],
        "up_command_seconds": 0.2,
        "definition": "wall clock from starting `docker compose up -d` until every health check passed; checks poll every 0.5 s from a throwaway container on the compose network, so resolution is about +/- 1 s"
      },
      "versions": {
        "restic": "0.19.1"
      },
      "setup": [
        {
          "exit_code": 0,
          "duration_s": 7.3,
          "reported": {
            "dataset": {
              "day1": {
                "files": 3348,
                "bytes": 334222462,
                "unique_file_bytes": 265791580,
                "duplicate_file_bytes": 68430882,
                "symlinks": 2,
                "docs_files": 3000,
                "docs_bytes": 21333065,
                "docs_zlib6_ratio": 3.15
              },
              "day2": {
                "files": 3369,
                "bytes": 342525272,
                "unique_file_bytes": 275936490,
                "duplicate_file_bytes": 66588782,
                "modified_files": 167,
                "added_files": 41,
                "deleted_files": 20,
                "modified_pct_of_day1_files": 4.99,
                "changed_bytes_whole_files": 64380805,
                "delta_bytes_new_content": 13838607
              },
              "seed": 20261019,
              "mtime_day1": 1790856000,
              "mtime_day2": 1790942400
            }
          },
          "output_tail": [],
          "description": "Generate the benchmark dataset with gen_dataset.py (standard library, deterministic, no downloads) into the tool's /data volume: /data/src with 3,348 files, about 334 MB (the 103 random-byte files of the file-sync tests, about 194.5 MB; 3,000 compressible text files of 0.5-24 KB; 41 exact duplicates including one of a 50 MB file; 200 identical template files; one 50 MB near-duplicate with four 4 KB regions changed; three oddly named files; two symlinks), plus the 'day 2' change set (about 5 % of the files modified, 41 added, 20 deleted) staged outside /data/src and the SHA-256 manifests of both days. Not measured: it runs before the idle window. The tool never sees the staging area."
        }
      ],
      "idle": {
        "per_service": {
          "restic": {
            "samples": 16,
            "mem_mean_mib": 0.59,
            "mem_max_mib": 0.8,
            "cpu_mean_pct": 0.41,
            "cpu_max_pct": 2.29
          }
        },
        "total": {
          "samples": 16,
          "dropped_for_total": 0,
          "mem_mean_mib": 0.59,
          "mem_max_mib": 0.8,
          "cpu_mean_pct": 0.41,
          "cpu_max_pct": 2.29
        },
        "window_seconds": 30.3,
        "series": [
          {
            "t_offset_s": 0.0,
            "total_mem_mib": 0.55,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 2.0,
            "total_mem_mib": 0.55,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 4.0,
            "total_mem_mib": 0.48,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 6.1,
            "total_mem_mib": 0.48,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 8.1,
            "total_mem_mib": 0.75,
            "total_cpu_pct": 2.02
          },
          {
            "t_offset_s": 10.1,
            "total_mem_mib": 0.65,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 12.1,
            "total_mem_mib": 0.65,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 14.1,
            "total_mem_mib": 0.54,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 16.1,
            "total_mem_mib": 0.46,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 18.2,
            "total_mem_mib": 0.8,
            "total_cpu_pct": 2.29
          },
          {
            "t_offset_s": 20.2,
            "total_mem_mib": 0.8,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 22.2,
            "total_mem_mib": 0.59,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 24.2,
            "total_mem_mib": 0.49,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 26.2,
            "total_mem_mib": 0.49,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 28.3,
            "total_mem_mib": 0.68,
            "total_cpu_pct": 2.2
          },
          {
            "t_offset_s": 30.3,
            "total_mem_mib": 0.48,
            "total_cpu_pct": 0.0
          }
        ],
        "memory_stat": {
          "read_at": "2026-10-19T03:59:08Z",
          "per_service": {
            "restic": {
              "cgroup": "v2",
              "anon_mib": 0.09,
              "file_mib": 0.0,
              "active_file_mib": 0.0,
              "inactive_file_mib": 0.0,
              "shmem_mib": 0.0
            }
          },
          "read_lag_s": 0.0,
          "method": "docker exec <container> cat /sys/fs/cgroup/memory.stat (cgroup v1: /sys/fs/cgroup/memory/memory.stat); MiB; splits what docker stats shows as one number into anon (heap) and file cache; a field that is absent was not printed by the kernel or not readable (see reason)",
          "taken": "right after the idle window"
        }
      },
      "settle": {
        "settled": true,
        "mode": "detected",
        "settle_seconds_used": 121.1,
        "settle_started_after_healthy_s": 7.6,
        "params": {
          "window_seconds": 60,
          "threshold_pct": 2.0,
          "threshold_min_mib": 10.0,
          "min_seconds": 120,
          "max_seconds": 600
        },
        "last_window": {
          "n": 30,
          "mean_mib": 0.62,
          "min_mib": 0.44,
          "max_mib": 0.84,
          "spread_mib": 0.39,
          "threshold_mib": 10.0
        },
        "rechecks": 0,
        "rule": "settled = over the last window_seconds the stack total varied (max - min) by less than max(threshold_pct of the window mean, threshold_min_mib); not before min_seconds, at the latest max_seconds, both counted from the end of the setup steps; the idle window must also be flat and agree with it. If max_seconds passes without settling, settled=false and the idle window is measured anyway."
      },
      "startup_peak": {
        "per_service_mem_mib": {
          "restic": 0.84
        },
        "total_mem_mib": 0.84,
        "samples": 64,
        "note": "highest value across docker stats samples (about one every 2-3 s) from healthy until the stack settled; shorter spikes between samples are not seen"
      },
      "timeline": {
        "step_s": 10,
        "t0": "healthy",
        "offsets_s": [
          10,
          20,
          30,
          40,
          50,
          60,
          70,
          80,
          90,
          100,
          110,
          120,
          130,
          140,
          150,
          160
        ],
        "total_mem_mib": [
          0.4,
          0.6,
          0.6,
          0.6,
          0.5,
          0.4,
          0.5,
          0.7,
          0.5,
          0.6,
          0.5,
          0.6,
          0.6,
          0.6,
          0.7,
          0.5
        ],
        "per_service_mem_mib": {
          "restic": [
            0.4,
            0.6,
            0.6,
            0.6,
            0.5,
            0.4,
            0.5,
            0.7,
            0.5,
            0.6,
            0.5,
            0.6,
            0.6,
            0.6,
            0.7,
            0.5
          ]
        }
      },
      "load": {
        "per_service": {
          "restic": {
            "samples": 12,
            "mem_mean_mib": 62.02,
            "mem_max_mib": 110.9,
            "cpu_mean_pct": 93.66,
            "cpu_max_pct": 136.48
          }
        },
        "total": {
          "samples": 12,
          "dropped_for_total": 0,
          "mem_mean_mib": 62.02,
          "mem_max_mib": 110.9,
          "cpu_mean_pct": 93.66,
          "cpu_max_pct": 136.48
        },
        "description": "The same fixed dataset for all three tools, run by one shell script inside the tool's own container (docker exec), so the backup is in the cgroup that docker stats measures. Steps: check /data/src against the day-1 SHA-256 manifest; create an encrypted local repository on a Docker volume (init); first backup of the 334 MB; the same backup into a second repository with compression off (restic and Borg; Kopia's default is already off); apply the day-2 change set (about 5 % of the files modified, 41 added, 20 deleted) and check the source against the day-2 manifest; second backup (incremental); a third backup with nothing changed; each tool's default integrity check and its deepest one (restic check [--read-data], borg check [--verify-data], kopia snapshot verify [--verify-files-percent=100]); restore the latest snapshot into an empty directory and compare the SHA-256 of every restored file and every symlink with the manifest. Each timed step is sampled every 0.1 s for the peak resident memory (VmHWM) and CPU time of the tool's processes. Local repository only: no network, cloud, S3, B2 or SFTP.",
        "duration_s": 21.7,
        "window": "from LAB_MARK load_start (or step start) until 2 s after the step ended",
        "reported_by_script": {
          "tool": "restic",
          "version": "0.19.1",
          "cli_startup_seconds": 0.01,
          "nproc": 2,
          "cpu_model": "INTEL(R) XEON(R) PLATINUM 8573C",
          "cpu_aes": true,
          "cpu_sha_ni": true,
          "cpu_avx2": true,
          "init": {
            "seconds": 2.57,
            "ok": true,
            "exit_code": 0,
            "repo_bytes": 603,
            "repo_disk_bytes": 1081344,
            "repo_files": 2,
            "peak_rss_mib": 91.5,
            "cpu_seconds": 2.52
          },
          "settings": "{\"version\":2,\"id\":\"360d39c38e1e59f6f69aa16a509827b6b26e1c9c7a813c791f9e60d7b758ae5a\",\"chunker_polynomial\":\"21c83f23e361df\"}",
          "first_backup": {
            "seconds": 3.29,
            "ok": true,
            "exit_code": 0,
            "repo_bytes": 209045321,
            "repo_disk_bytes": 210161664,
            "repo_files": 17,
            "peak_rss_mib": 126.9,
            "cpu_seconds": 3.11,
            "files": 3348,
            "source_bytes": 334222462
          },
          "first_backup_no_compression": {
            "seconds": 3.1,
            "ok": true,
            "exit_code": 0,
            "repo_bytes": 223019682,
            "repo_disk_bytes": 224133120,
            "repo_files": 18,
            "peak_rss_mib": 108.8,
            "cpu_seconds": 2.39,
            "files": 3348,
            "source_bytes": 334222462,
            "compression": "off"
          },
          "incremental": {
            "seconds": 1.09,
            "ok": true,
            "exit_code": 0,
            "repo_bytes": 224718798,
            "repo_disk_bytes": 225849344,
            "repo_files": 21,
            "peak_rss_mib": 105.4,
            "cpu_seconds": 0.94,
            "files": 3369,
            "source_bytes": 342525272,
            "changed_files": 208,
            "changed_bytes_whole_files": 64380805,
            "deleted_files": 20,
            "repo_growth_bytes": 15673477
          },
          "unchanged_rerun": {
            "seconds": 0.75,
            "ok": true,
            "exit_code": 0,
            "repo_bytes": 224719195,
            "repo_disk_bytes": 225853440,
            "repo_files": 22,
            "peak_rss_mib": 62.7,
            "cpu_seconds": 0.45,
            "files": 3369,
            "source_bytes": 342525272,
            "repo_growth_bytes": 397
          },
          "check_default": {
            "seconds": 0.71,
            "ok": true,
            "exit_code": 0,
            "peak_rss_mib": 60.7,
            "cpu_seconds": 0.44
          },
          "check_deep": {
            "seconds": 1.14,
            "ok": true,
            "exit_code": 0,
            "peak_rss_mib": 89.3,
            "cpu_seconds": 1.26
          },
          "restore": {
            "seconds": 1.22,
            "ok": true,
            "exit_code": 0,
            "files_verified": 3369,
            "files_expected": 3369,
            "files_restored": 3369,
            "missing_or_different": 0,
            "unexpected": 0,
            "sha256_match_all": true,
            "symlinks_match": true,
            "restored_bytes": 342525272,
            "peak_rss_mib": 135.8,
            "cpu_seconds": 1.25
          },
          "script_seconds": 21.74,
          "all_ok": true
        },
        "output_tail": [
          "source day 1: 3348 files, 334222462 bytes, matches manifest-day1 (3348 hashes)",
          "init: 2.57s",
          "first backup: 3.29s, repository 209045321 bytes, peak RSS 126.9 MiB",
          "first backup, compression off: 3.10s, repository 223019682 bytes",
          "source day 2: 3369 files, 342525272 bytes (208 files written or replaced, 20 deleted), matches manifest-day2",
          "incremental: 1.09s (first backup 3.29s), repository grew by 15673477 bytes, peak RSS 105.4 MiB",
          "unchanged re-run: 0.75s, repository grew by 397 bytes",
          "check: default {\"seconds\": 0.71, \"ok\": true, \"exit_code\": 0, \"peak_rss_mib\": 60.7, \"cpu_seconds\": 0.44}, deep {\"seconds\": 1.14, \"ok\": true, \"exit_code\": 0, \"peak_rss_mib\": 89.3, \"cpu_seconds\": 1.26}",
          "rm: can't remove '/restore': Resource busy",
          "restore: 1.22s, 3369 of 3369 files verified by sha256, symlinks match: true, peak RSS 135.8 MiB"
        ],
        "series": [
          {
            "t_offset_s": 0.0,
            "total_mem_mib": 2.06,
            "total_cpu_pct": 100.56
          },
          {
            "t_offset_s": 2.0,
            "total_mem_mib": 72.08,
            "total_cpu_pct": 106.9
          },
          {
            "t_offset_s": 4.0,
            "total_mem_mib": 44.47,
            "total_cpu_pct": 64.53
          },
          {
            "t_offset_s": 6.0,
            "total_mem_mib": 100.5,
            "total_cpu_pct": 100.69
          },
          {
            "t_offset_s": 8.0,
            "total_mem_mib": 81.82,
            "total_cpu_pct": 105.77
          },
          {
            "t_offset_s": 10.0,
            "total_mem_mib": 95.07,
            "total_cpu_pct": 106.26
          },
          {
            "t_offset_s": 12.0,
            "total_mem_mib": 110.9,
            "total_cpu_pct": 104.23
          },
          {
            "t_offset_s": 14.0,
            "total_mem_mib": 57.12,
            "total_cpu_pct": 102.86
          },
          {
            "t_offset_s": 16.0,
            "total_mem_mib": 57.8,
            "total_cpu_pct": 95.55
          },
          {
            "t_offset_s": 18.1,
            "total_mem_mib": 57.63,
            "total_cpu_pct": 136.48
          },
          {
            "t_offset_s": 20.1,
            "total_mem_mib": 33.15,
            "total_cpu_pct": 100.05
          },
          {
            "t_offset_s": 22.1,
            "total_mem_mib": 31.59,
            "total_cpu_pct": 0.0
          }
        ],
        "series_start_offset_s": 1.9,
        "peak_note": "peak = highest value seen across docker stats samples (about one every 2-3 s); shorter spikes between samples are not seen",
        "peak_memory_stat": {
          "read_at": "2026-10-19T03:59:22Z",
          "per_service": {
            "restic": {
              "cgroup": "v2",
              "anon_mib": 0.76,
              "file_mib": 475.09,
              "active_file_mib": 0.0,
              "inactive_file_mib": 475.09,
              "shmem_mib": 0.0
            }
          },
          "read_lag_s": 0.1,
          "series_index": 6,
          "sample_t_offset_s": 12.0,
          "total_mem_mib": 110.9
        }
      },
      "container_end_state": {
        "restic": {
          "restart_count": 0,
          "oom_killed": false
        }
      },
      "teardown": {
        "done": true,
        "volumes_removed": true,
        "images_removed": false,
        "error": null
      },
      "warnings": [],
      "finished_at": "2026-10-19T03:59:48Z",
      "elapsed_seconds": 204.0
    }
  ],
  "load_defined": true,
  "notes": [
    "Host: a GitHub Actions runner (ubuntu-latest, x86_64, 2 vCPU, 8 GB RAM; the CPU model varies between runs and each run records its own, with its AES and SHA-NI flags in load.reported_by_script). Times and sizes are valid for that host only.",
    "Image: restic/restic:0.19.1, an Alpine image whose entrypoint is replaced by `sleep infinity` (it is a command line tool; readers run it from cron or a systemd timer). The container's idle RAM is that of `sleep`.",
    "What is measured (load.reported_by_script, one value per run; the harness does not take medians of these keys, take them over the runs by hand): `init`, `first_backup`, `first_backup_no_compression`, `incremental`, `unchanged_rerun`, `check_default`, `check_deep` and `restore`, each with `seconds` (from /proc/uptime, 0.01 s resolution), `ok`, `exit_code`, `peak_rss_mib` (highest sum of VmHWM of the tool's processes, sampled every 0.1 s; it does not include page cache) and `cpu_seconds` (user + system time of those processes, sampled the same way); the backup phases also `repo_bytes` (sum of the sizes of all files in the repository), `repo_disk_bytes` (disk blocks used, `du`), `repo_files`, `files` and `source_bytes`; `incremental` adds `repo_growth_bytes`, `changed_files`, `changed_bytes_whole_files`; `restore` has `files_verified`, `files_expected`, `sha256_match_all`, `symlinks_match`. `tool`, `version` (read from the binary), `cli_startup_seconds` (`--version`), `nproc`, `cpu_model`, `cpu_aes`, `cpu_sha_ni`. The setup step reports the dataset (`dataset`: counts, bytes, duplicate bytes, day-2 changes, delta_bytes_new_content).",
    "RAM: use `peak_rss_mib` for how much memory the tool needs. The harness figures for the container (`load.total`, `peak_mem_mib`) are docker stats, which count the page cache of the files read and written by the backup that is not yet reclaimable, so they are higher; `peak_memory_stat` in each run splits the container's memory into anon and file.",
    "Fairness: the same files (generated, deterministic, SHA-256 manifests made by the generator, not by a tool), the same machine, the same order of phases, a fresh container and empty volumes for every run, one tool per run. All three read the dataset from the same kind of Docker volume (page cache warm after the generator wrote it, so reads are not disk-bound) and write the repository to a Docker volume on the same disk. File mtimes are fixed so change detection (size + mtime) behaves identically in every run.",
    "Not measured: remote repositories (SFTP, S3, B2, REST), network cost, prune/forget/compact/maintenance time (Kopia runs its quick maintenance automatically after snapshots, as it does by default, and `repository create` runs a first maintenance; both are inside the timed commands), mount/FUSE access, multiple clients, a repository that has grown over months, cold caches (a restart of the container deletes restic's and Borg's cache directories; this test keeps them for the whole run), file ownership and extended attributes, very large single files, parallel jobs. `check_default` is not the same depth in the three tools: restic check reads repository structure and metadata, borg check verifies repository and archive metadata, kopia snapshot verify checks that every object exists without downloading file contents; `check_deep` reads and verifies all data in all three.",
    "restic 0.19.1 (Docker Hub restic/restic:0.19.1, GitHub release of 5 Jul 2026, `latest` has the same digest). Repository format version 2 (the default 'stable'), so compression is on: --compression auto, the default (zstd; data that does not compress is stored as it is). Encryption is always on in restic: AES-256 in counter mode with Poly1305-AES authentication, key derived from the password with scrypt (the cost of `init`). Chunker: content-defined, 512 KiB-8 MiB, average 1 MiB. Default parallelism (GOMAXPROCS). `first_backup_no_compression` uses --compression off on a second repository.",
    "Commands (all from bench.sh): `restic init -r /repo`; `restic backup -r /repo /data/src`; `restic backup -r ALT --compression off /data/src`; `restic check -r /repo` and `restic check -r /repo --read-data`; `restic restore latest -r /repo --target /restore`. restic keeps a local cache in the container (not in the repository size)."
  ],
  "warnings": [],
  "files": {
    "compose": "backup-restic.compose.yaml",
    "env": "backup-restic.env"
  },
  "definitions": {
    "memory": "cgroup working set as reported by `docker stats` (usage minus inactive file cache), MiB = 2^20 bytes",
    "cpu": "percent of ONE core as reported by `docker stats` (4 busy cores = 400)",
    "total": "stats over the sum of all containers at the same instant",
    "sample_interval": "docker stats --no-stream takes about 1-2 s per call, so samples are 2-3 s apart",
    "runs": "each run is a full fresh cycle (up, health, setup, settle, idle, load, down -v); the published figure is the median over the runs, shown with the min-max range; see summary.rule"
  }
}
