{
  "schema_version": 3,
  "app": "backup-borg",
  "display_name": "BorgBackup backup benchmark",
  "upstream_repo": "https://github.com/borgbackup/borg",
  "docs_url": "https://borgbackup.readthedocs.io/en/1.4-maint/",
  "status": "ok",
  "summary": {
    "primary_service": "borg",
    "app_version": "1.4.5",
    "app_version_pinned": true,
    "runs": 3,
    "publishable": true,
    "publishable_note": null,
    "idle_total_mem_mib": 1.84,
    "idle_total_cpu_pct": 0.39,
    "peak_total_mem_mib": 78.59,
    "peak_is_under_load": true,
    "startup_seconds": 5.4,
    "startup_peak_total_mem_mib": 2.07,
    "settled": true,
    "settle_seconds_used": 120.6,
    "peak_max_mib": 83.04,
    "sizing_peak_mib": 83.04,
    "idle_mem_mib": {
      "median": 1.84,
      "min": 1.79,
      "max": 1.88,
      "values": [
        1.88,
        1.79,
        1.84
      ]
    },
    "idle_cpu_pct": {
      "median": 0.39,
      "min": 0.39,
      "max": 0.39,
      "values": [
        0.39,
        0.39,
        0.39
      ]
    },
    "peak_mem_mib": {
      "median": 78.59,
      "min": 74.69,
      "max": 83.04,
      "values": [
        74.69,
        78.59,
        83.04
      ]
    },
    "startup_peak_mem_mib": {
      "median": 2.07,
      "min": 2.06,
      "max": 2.07,
      "values": [
        2.06,
        2.07,
        2.07
      ]
    },
    "startup_s": {
      "median": 5.4,
      "min": 5.4,
      "max": 5.4,
      "values": [
        5.4,
        5.4,
        5.4
      ]
    },
    "settled_after_healthy_s": {
      "median": 128.5,
      "min": 128.5,
      "max": 130.6,
      "values": [
        130.6,
        128.5,
        128.5
      ]
    },
    "settled_runs": 3,
    "per_service": {
      "borg": {
        "idle_mem_mib": {
          "median": 1.84,
          "min": 1.79,
          "max": 1.88,
          "values": [
            1.88,
            1.79,
            1.84
          ]
        },
        "peak_mem_mib": {
          "median": 78.59,
          "min": 74.69,
          "max": 83.04,
          "values": [
            74.69,
            78.59,
            83.04
          ]
        }
      }
    },
    "variance_flag": null,
    "representative_run": 3,
    "cpu_models": [
      "AMD EPYC 9V74 80-Core Processor"
    ],
    "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:28Z",
    "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": "AMD EPYC 9V74 80-Core Processor",
    "cpu_model_note": null,
    "cores": 2,
    "mem_total_mib": 7938,
    "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": 0.2,
  "finished_at": "2026-10-19T03:59:49Z",
  "elapsed_seconds": 621.6,
  "services": {
    "borg": {
      "image_ref": "python:3.12-slim",
      "image_repo": "python",
      "image_tag": "3.12-slim",
      "digest": "sha256:a6e34c598f2467ed0e9a8d349809fcd8b5c603269512df273a0bb1784edc11b1",
      "digest_note": null,
      "version": "1.4.5",
      "version_source": "exec",
      "version_pinned": true,
      "version_note": "version taken from the running app (prefer_exec), not from the image label",
      "resolved_tag": "3.12-slim",
      "resolved_tag_note": null,
      "image_size": {
        "unpacked_mb": 119.0,
        "unpacked_source": "docker image ls Size: unpacked image on the daemon host, rounded by docker to 3 significant figures",
        "compressed_mb": 43.3,
        "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": "python:3.12-slim",
      "compose_comment": "sha256:a6e34c598f2467ed0e9a8d349809fcd8b5c603269512df273a0bb1784edc11b1  (version 1.4.5, from exec)",
      "restart_count": 0,
      "oom_killed": false
    }
  },
  "runs": [
    {
      "run": 1,
      "started_at": "2026-10-19T03:49:33Z",
      "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": "AMD EPYC 9V74 80-Core Processor",
        "cpu_model_note": null,
        "cores": 2,
        "mem_total_mib": 7938,
        "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-borg-borg-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": {
        "borg": "1.4.5"
      },
      "setup": [
        {
          "exit_code": 0,
          "duration_s": 7.9,
          "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": {
          "borg": {
            "samples": 16,
            "mem_mean_mib": 1.88,
            "mem_max_mib": 2.05,
            "cpu_mean_pct": 0.39,
            "cpu_max_pct": 2.12
          }
        },
        "total": {
          "samples": 16,
          "dropped_for_total": 0,
          "mem_mean_mib": 1.88,
          "mem_max_mib": 2.05,
          "cpu_mean_pct": 0.39,
          "cpu_max_pct": 2.12
        },
        "window_seconds": 30.3,
        "series": [
          {
            "t_offset_s": 0.0,
            "total_mem_mib": 1.86,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 2.0,
            "total_mem_mib": 1.7,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 4.0,
            "total_mem_mib": 1.7,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 6.1,
            "total_mem_mib": 2.05,
            "total_cpu_pct": 2.12
          },
          {
            "t_offset_s": 8.1,
            "total_mem_mib": 2.05,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 10.1,
            "total_mem_mib": 1.81,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 12.1,
            "total_mem_mib": 1.98,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 14.1,
            "total_mem_mib": 1.98,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 16.1,
            "total_mem_mib": 1.85,
            "total_cpu_pct": 2.09
          },
          {
            "t_offset_s": 18.1,
            "total_mem_mib": 1.85,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 20.2,
            "total_mem_mib": 1.85,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 22.2,
            "total_mem_mib": 1.95,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 24.2,
            "total_mem_mib": 1.95,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 26.2,
            "total_mem_mib": 1.98,
            "total_cpu_pct": 2.09
          },
          {
            "t_offset_s": 28.2,
            "total_mem_mib": 1.9,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 30.3,
            "total_mem_mib": 1.66,
            "total_cpu_pct": 0.0
          }
        ],
        "memory_stat": {
          "read_at": "2026-10-19T03:52:20Z",
          "per_service": {
            "borg": {
              "cgroup": "v2",
              "anon_mib": 0.22,
              "file_mib": 29.83,
              "active_file_mib": 0.0,
              "inactive_file_mib": 29.83,
              "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.0,
        "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": 1.82,
          "min_mib": 1.66,
          "max_mib": 2.06,
          "spread_mib": 0.4,
          "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": {
          "borg": 2.06
        },
        "total_mem_mib": 2.06,
        "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": [
          1.7,
          1.8,
          1.7,
          1.8,
          1.8,
          1.8,
          1.9,
          1.9,
          1.7,
          1.9,
          1.9,
          1.8,
          1.8,
          1.9,
          1.9,
          1.9,
          1.7
        ],
        "per_service_mem_mib": {
          "borg": [
            1.7,
            1.8,
            1.7,
            1.8,
            1.8,
            1.8,
            1.9,
            1.9,
            1.7,
            1.9,
            1.9,
            1.8,
            1.8,
            1.9,
            1.9,
            1.9,
            1.7
          ]
        }
      },
      "load": {
        "per_service": {
          "borg": {
            "samples": 13,
            "mem_mean_mib": 43.29,
            "mem_max_mib": 74.69,
            "cpu_mean_pct": 64.74,
            "cpu_max_pct": 105.53
          }
        },
        "total": {
          "samples": 13,
          "dropped_for_total": 0,
          "mem_mean_mib": 43.29,
          "mem_max_mib": 74.69,
          "cpu_mean_pct": 64.74,
          "cpu_max_pct": 105.53
        },
        "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": 23.7,
        "window": "from LAB_MARK load_start (or step start) until 2 s after the step ended",
        "reported_by_script": {
          "tool": "borg",
          "version": "1.4.5",
          "cli_startup_seconds": 0.52,
          "nproc": 2,
          "cpu_model": "AMD EPYC 9V74 80-Core Processor",
          "cpu_aes": true,
          "cpu_sha_ni": true,
          "cpu_avx2": true,
          "init": {
            "seconds": 0.63,
            "ok": true,
            "exit_code": 0,
            "repo_bytes": 43118,
            "repo_disk_bytes": 86016,
            "repo_files": 8,
            "peak_rss_mib": 82.3,
            "cpu_seconds": 0.5
          },
          "settings": "Encrypted: Yes (repokey BLAKE2b)",
          "first_backup": {
            "seconds": 4.29,
            "ok": true,
            "exit_code": 0,
            "repo_bytes": 226852096,
            "repo_disk_bytes": 226910208,
            "repo_files": 12,
            "peak_rss_mib": 90.6,
            "cpu_seconds": 2.02,
            "files": 3348,
            "source_bytes": 334222462
          },
          "first_backup_no_compression": {
            "seconds": 5.25,
            "ok": true,
            "exit_code": 0,
            "repo_bytes": 236151972,
            "repo_disk_bytes": 236208128,
            "repo_files": 12,
            "peak_rss_mib": 90.5,
            "cpu_seconds": 2.1,
            "files": 3348,
            "source_bytes": 334222462,
            "compression": "off"
          },
          "incremental": {
            "seconds": 1.32,
            "ok": true,
            "exit_code": 0,
            "repo_bytes": 251900563,
            "repo_disk_bytes": 251973632,
            "repo_files": 16,
            "peak_rss_mib": 89.7,
            "cpu_seconds": 0.91,
            "files": 3369,
            "source_bytes": 342525272,
            "changed_files": 208,
            "changed_bytes_whole_files": 64380805,
            "deleted_files": 20,
            "repo_growth_bytes": 25048467
          },
          "unchanged_rerun": {
            "seconds": 0.8,
            "ok": true,
            "exit_code": 0,
            "repo_bytes": 251902025,
            "repo_disk_bytes": 251990016,
            "repo_files": 20,
            "peak_rss_mib": 82.3,
            "cpu_seconds": 0.69,
            "files": 3369,
            "source_bytes": 342525272,
            "repo_growth_bytes": 1462
          },
          "check_default": {
            "seconds": 1.64,
            "ok": true,
            "exit_code": 0,
            "peak_rss_mib": 82.1,
            "cpu_seconds": 0.66
          },
          "check_deep": {
            "seconds": 2.44,
            "ok": true,
            "exit_code": 0,
            "peak_rss_mib": 82.4,
            "cpu_seconds": 1.5
          },
          "restore": {
            "seconds": 2.38,
            "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": 86.7,
            "cpu_seconds": 1.93
          },
          "script_seconds": 23.66,
          "all_ok": true
        },
        "output_tail": [
          "source day 1: 3348 files, 334222462 bytes, matches manifest-day1 (3348 hashes)",
          "init: 0.63s",
          "first backup: 4.29s, repository 226852096 bytes, peak RSS 90.6 MiB",
          "first backup, compression off: 5.25s, repository 236151972 bytes",
          "source day 2: 3369 files, 342525272 bytes (208 files written or replaced, 20 deleted), matches manifest-day2",
          "incremental: 1.32s (first backup 4.29s), repository grew by 25048467 bytes, peak RSS 89.7 MiB",
          "unchanged re-run: 0.80s, repository grew by 1462 bytes",
          "check: default {\"seconds\": 1.64, \"ok\": true, \"exit_code\": 0, \"peak_rss_mib\": 82.1, \"cpu_seconds\": 0.66}, deep {\"seconds\": 2.44, \"ok\": true, \"exit_code\": 0, \"peak_rss_mib\": 82.4, \"cpu_seconds\": 1.50}",
          "rm: cannot remove '/restore': Device or resource busy",
          "restore: 2.38s, 3369 of 3369 files verified by sha256, symlinks match: true, peak RSS 86.7 MiB"
        ],
        "series": [
          {
            "t_offset_s": 0.0,
            "total_mem_mib": 3.34,
            "total_cpu_pct": 105.53
          },
          {
            "t_offset_s": 2.0,
            "total_mem_mib": 69.15,
            "total_cpu_pct": 102.71
          },
          {
            "t_offset_s": 4.0,
            "total_mem_mib": 63.03,
            "total_cpu_pct": 27.28
          },
          {
            "t_offset_s": 6.0,
            "total_mem_mib": 32.83,
            "total_cpu_pct": 65.1
          },
          {
            "t_offset_s": 8.0,
            "total_mem_mib": 70.54,
            "total_cpu_pct": 73.68
          },
          {
            "t_offset_s": 10.0,
            "total_mem_mib": 74.69,
            "total_cpu_pct": 4.2
          },
          {
            "t_offset_s": 12.0,
            "total_mem_mib": 6.26,
            "total_cpu_pct": 16.17
          },
          {
            "t_offset_s": 14.0,
            "total_mem_mib": 67.86,
            "total_cpu_pct": 77.45
          },
          {
            "t_offset_s": 16.0,
            "total_mem_mib": 37.42,
            "total_cpu_pct": 98.31
          },
          {
            "t_offset_s": 18.0,
            "total_mem_mib": 52.52,
            "total_cpu_pct": 65.39
          },
          {
            "t_offset_s": 20.0,
            "total_mem_mib": 45.37,
            "total_cpu_pct": 103.35
          },
          {
            "t_offset_s": 22.1,
            "total_mem_mib": 20.86,
            "total_cpu_pct": 100.39
          },
          {
            "t_offset_s": 24.1,
            "total_mem_mib": 18.89,
            "total_cpu_pct": 2.1
          }
        ],
        "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:32Z",
          "per_service": {
            "borg": {
              "cgroup": "v2",
              "anon_mib": 62.8,
              "file_mib": 332.02,
              "active_file_mib": 0.12,
              "inactive_file_mib": 331.91,
              "shmem_mib": 0.0
            }
          },
          "read_lag_s": 0.0,
          "series_index": 5,
          "sample_t_offset_s": 10.0,
          "total_mem_mib": 74.69
        }
      },
      "container_end_state": {
        "borg": {
          "restart_count": 0,
          "oom_killed": false
        }
      },
      "teardown": {
        "done": true,
        "volumes_removed": true,
        "images_removed": false,
        "error": null
      },
      "warnings": [],
      "finished_at": "2026-10-19T03:53:00Z",
      "elapsed_seconds": 206.9
    },
    {
      "run": 2,
      "started_at": "2026-10-19T03:53:00Z",
      "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": "AMD EPYC 9V74 80-Core Processor",
        "cpu_model_note": null,
        "cores": 2,
        "mem_total_mib": 7938,
        "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-borg-borg-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": {
        "borg": "1.4.5"
      },
      "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": {
          "borg": {
            "samples": 16,
            "mem_mean_mib": 1.79,
            "mem_max_mib": 1.97,
            "cpu_mean_pct": 0.39,
            "cpu_max_pct": 2.09
          }
        },
        "total": {
          "samples": 16,
          "dropped_for_total": 0,
          "mem_mean_mib": 1.79,
          "mem_max_mib": 1.97,
          "cpu_mean_pct": 0.39,
          "cpu_max_pct": 2.09
        },
        "window_seconds": 30.2,
        "series": [
          {
            "t_offset_s": 0.0,
            "total_mem_mib": 1.59,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 2.0,
            "total_mem_mib": 1.59,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 4.0,
            "total_mem_mib": 1.97,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 6.0,
            "total_mem_mib": 1.97,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 8.1,
            "total_mem_mib": 1.93,
            "total_cpu_pct": 2.08
          },
          {
            "t_offset_s": 10.1,
            "total_mem_mib": 1.82,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 12.1,
            "total_mem_mib": 1.71,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 14.1,
            "total_mem_mib": 1.84,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 16.1,
            "total_mem_mib": 1.84,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 18.1,
            "total_mem_mib": 1.69,
            "total_cpu_pct": 2.09
          },
          {
            "t_offset_s": 20.2,
            "total_mem_mib": 1.69,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 22.2,
            "total_mem_mib": 1.69,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 24.2,
            "total_mem_mib": 1.79,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 26.2,
            "total_mem_mib": 1.79,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 28.2,
            "total_mem_mib": 1.87,
            "total_cpu_pct": 2.06
          },
          {
            "t_offset_s": 30.2,
            "total_mem_mib": 1.87,
            "total_cpu_pct": 0.0
          }
        ],
        "memory_stat": {
          "read_at": "2026-10-19T03:55:45Z",
          "per_service": {
            "borg": {
              "cgroup": "v2",
              "anon_mib": 0.21,
              "file_mib": 29.8,
              "active_file_mib": 0.0,
              "inactive_file_mib": 29.8,
              "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": 120.6,
        "settle_started_after_healthy_s": 7.9,
        "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": 1.76,
          "min_mib": 1.59,
          "max_mib": 2.07,
          "spread_mib": 0.47,
          "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": {
          "borg": 2.07
        },
        "total_mem_mib": 2.07,
        "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": [
          1.8,
          1.9,
          1.8,
          1.9,
          1.7,
          1.9,
          1.9,
          1.7,
          1.9,
          1.9,
          1.7,
          1.7,
          1.7,
          1.9,
          1.8,
          1.8
        ],
        "per_service_mem_mib": {
          "borg": [
            1.8,
            1.9,
            1.8,
            1.9,
            1.7,
            1.9,
            1.9,
            1.7,
            1.9,
            1.9,
            1.7,
            1.7,
            1.7,
            1.9,
            1.8,
            1.8
          ]
        }
      },
      "load": {
        "per_service": {
          "borg": {
            "samples": 13,
            "mem_mean_mib": 42.53,
            "mem_max_mib": 78.59,
            "cpu_mean_pct": 57.03,
            "cpu_max_pct": 105.53
          }
        },
        "total": {
          "samples": 13,
          "dropped_for_total": 0,
          "mem_mean_mib": 42.53,
          "mem_max_mib": 78.59,
          "cpu_mean_pct": 57.03,
          "cpu_max_pct": 105.53
        },
        "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": 23.3,
        "window": "from LAB_MARK load_start (or step start) until 2 s after the step ended",
        "reported_by_script": {
          "tool": "borg",
          "version": "1.4.5",
          "cli_startup_seconds": 0.51,
          "nproc": 2,
          "cpu_model": "AMD EPYC 9V74 80-Core Processor",
          "cpu_aes": true,
          "cpu_sha_ni": true,
          "cpu_avx2": true,
          "init": {
            "seconds": 0.64,
            "ok": true,
            "exit_code": 0,
            "repo_bytes": 43118,
            "repo_disk_bytes": 86016,
            "repo_files": 8,
            "peak_rss_mib": 82.4,
            "cpu_seconds": 0.48
          },
          "settings": "Encrypted: Yes (repokey BLAKE2b)",
          "first_backup": {
            "seconds": 4.15,
            "ok": true,
            "exit_code": 0,
            "repo_bytes": 221290520,
            "repo_disk_bytes": 221351936,
            "repo_files": 12,
            "peak_rss_mib": 83.5,
            "cpu_seconds": 1.93,
            "files": 3348,
            "source_bytes": 334222462
          },
          "first_backup_no_compression": {
            "seconds": 5.05,
            "ok": true,
            "exit_code": 0,
            "repo_bytes": 230931785,
            "repo_disk_bytes": 230989824,
            "repo_files": 12,
            "peak_rss_mib": 90.2,
            "cpu_seconds": 2.05,
            "files": 3348,
            "source_bytes": 334222462,
            "compression": "off"
          },
          "incremental": {
            "seconds": 1.21,
            "ok": true,
            "exit_code": 0,
            "repo_bytes": 244757770,
            "repo_disk_bytes": 244834304,
            "repo_files": 16,
            "peak_rss_mib": 90.0,
            "cpu_seconds": 0.9,
            "files": 3369,
            "source_bytes": 342525272,
            "changed_files": 208,
            "changed_bytes_whole_files": 64380805,
            "deleted_files": 20,
            "repo_growth_bytes": 23467250
          },
          "unchanged_rerun": {
            "seconds": 0.79,
            "ok": true,
            "exit_code": 0,
            "repo_bytes": 244759304,
            "repo_disk_bytes": 244850688,
            "repo_files": 20,
            "peak_rss_mib": 82.5,
            "cpu_seconds": 0.7,
            "files": 3369,
            "source_bytes": 342525272,
            "repo_growth_bytes": 1534
          },
          "check_default": {
            "seconds": 1.79,
            "ok": true,
            "exit_code": 0,
            "peak_rss_mib": 82.3,
            "cpu_seconds": 0.72
          },
          "check_deep": {
            "seconds": 2.34,
            "ok": true,
            "exit_code": 0,
            "peak_rss_mib": 82.3,
            "cpu_seconds": 1.43
          },
          "restore": {
            "seconds": 2.48,
            "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": 89.0,
            "cpu_seconds": 1.92
          },
          "script_seconds": 23.3,
          "all_ok": true
        },
        "output_tail": [
          "source day 1: 3348 files, 334222462 bytes, matches manifest-day1 (3348 hashes)",
          "init: 0.64s",
          "first backup: 4.15s, repository 221290520 bytes, peak RSS 83.5 MiB",
          "first backup, compression off: 5.05s, repository 230931785 bytes",
          "source day 2: 3369 files, 342525272 bytes (208 files written or replaced, 20 deleted), matches manifest-day2",
          "incremental: 1.21s (first backup 4.15s), repository grew by 23467250 bytes, peak RSS 90.0 MiB",
          "unchanged re-run: 0.79s, repository grew by 1534 bytes",
          "check: default {\"seconds\": 1.79, \"ok\": true, \"exit_code\": 0, \"peak_rss_mib\": 82.3, \"cpu_seconds\": 0.72}, deep {\"seconds\": 2.34, \"ok\": true, \"exit_code\": 0, \"peak_rss_mib\": 82.3, \"cpu_seconds\": 1.43}",
          "rm: cannot remove '/restore': Device or resource busy",
          "restore: 2.48s, 3369 of 3369 files verified by sha256, symlinks match: true, peak RSS 89.0 MiB"
        ],
        "series": [
          {
            "t_offset_s": 0.0,
            "total_mem_mib": 3.35,
            "total_cpu_pct": 105.53
          },
          {
            "t_offset_s": 2.0,
            "total_mem_mib": 61.18,
            "total_cpu_pct": 101.65
          },
          {
            "t_offset_s": 4.0,
            "total_mem_mib": 78.59,
            "total_cpu_pct": 14.56
          },
          {
            "t_offset_s": 6.0,
            "total_mem_mib": 4.29,
            "total_cpu_pct": 83.92
          },
          {
            "t_offset_s": 8.0,
            "total_mem_mib": 67.52,
            "total_cpu_pct": 69.6
          },
          {
            "t_offset_s": 10.0,
            "total_mem_mib": 67.56,
            "total_cpu_pct": 3.74
          },
          {
            "t_offset_s": 12.0,
            "total_mem_mib": 6.64,
            "total_cpu_pct": 15.79
          },
          {
            "t_offset_s": 14.0,
            "total_mem_mib": 62.95,
            "total_cpu_pct": 84.6
          },
          {
            "t_offset_s": 16.0,
            "total_mem_mib": 50.38,
            "total_cpu_pct": 51.98
          },
          {
            "t_offset_s": 18.0,
            "total_mem_mib": 54.2,
            "total_cpu_pct": 31.82
          },
          {
            "t_offset_s": 20.1,
            "total_mem_mib": 58.76,
            "total_cpu_pct": 91.75
          },
          {
            "t_offset_s": 22.1,
            "total_mem_mib": 18.74,
            "total_cpu_pct": 86.51
          },
          {
            "t_offset_s": 24.1,
            "total_mem_mib": 18.74,
            "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:51Z",
          "per_service": {
            "borg": {
              "cgroup": "v2",
              "anon_mib": 67.68,
              "file_mib": 311.74,
              "active_file_mib": 0.01,
              "inactive_file_mib": 311.74,
              "shmem_mib": 0.0
            }
          },
          "read_lag_s": 0.0,
          "series_index": 2,
          "sample_t_offset_s": 4.0,
          "total_mem_mib": 78.59
        }
      },
      "container_end_state": {
        "borg": {
          "restart_count": 0,
          "oom_killed": false
        }
      },
      "teardown": {
        "done": true,
        "volumes_removed": true,
        "images_removed": false,
        "error": null
      },
      "warnings": [],
      "finished_at": "2026-10-19T03:56:25Z",
      "elapsed_seconds": 204.9
    },
    {
      "run": 3,
      "started_at": "2026-10-19T03:56:25Z",
      "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": "AMD EPYC 9V74 80-Core Processor",
        "cpu_model_note": null,
        "cores": 2,
        "mem_total_mib": 7938,
        "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-borg-borg-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": {
        "borg": "1.4.5"
      },
      "setup": [
        {
          "exit_code": 0,
          "duration_s": 7.4,
          "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": {
          "borg": {
            "samples": 16,
            "mem_mean_mib": 1.84,
            "mem_max_mib": 2.04,
            "cpu_mean_pct": 0.39,
            "cpu_max_pct": 2.1
          }
        },
        "total": {
          "samples": 16,
          "dropped_for_total": 0,
          "mem_mean_mib": 1.84,
          "mem_max_mib": 2.04,
          "cpu_mean_pct": 0.39,
          "cpu_max_pct": 2.1
        },
        "window_seconds": 30.2,
        "series": [
          {
            "t_offset_s": 0.0,
            "total_mem_mib": 1.85,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 2.0,
            "total_mem_mib": 1.85,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 4.0,
            "total_mem_mib": 1.94,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 6.0,
            "total_mem_mib": 1.94,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 8.1,
            "total_mem_mib": 1.82,
            "total_cpu_pct": 2.09
          },
          {
            "t_offset_s": 10.1,
            "total_mem_mib": 1.82,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 12.1,
            "total_mem_mib": 1.82,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 14.1,
            "total_mem_mib": 1.69,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 16.1,
            "total_mem_mib": 1.69,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 18.1,
            "total_mem_mib": 1.78,
            "total_cpu_pct": 2.06
          },
          {
            "t_offset_s": 20.2,
            "total_mem_mib": 1.75,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 22.2,
            "total_mem_mib": 1.75,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 24.2,
            "total_mem_mib": 1.88,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 26.2,
            "total_mem_mib": 1.88,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 28.2,
            "total_mem_mib": 2.04,
            "total_cpu_pct": 2.1
          },
          {
            "t_offset_s": 30.2,
            "total_mem_mib": 2.0,
            "total_cpu_pct": 0.0
          }
        ],
        "memory_stat": {
          "read_at": "2026-10-19T03:59:10Z",
          "per_service": {
            "borg": {
              "cgroup": "v2",
              "anon_mib": 0.21,
              "file_mib": 29.8,
              "active_file_mib": 0.0,
              "inactive_file_mib": 29.8,
              "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": 120.5,
        "settle_started_after_healthy_s": 8.0,
        "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": 1.85,
          "min_mib": 1.61,
          "max_mib": 2.07,
          "spread_mib": 0.46,
          "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": {
          "borg": 2.07
        },
        "total_mem_mib": 2.07,
        "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": [
          1.8,
          1.7,
          1.9,
          1.9,
          1.9,
          1.8,
          1.8,
          1.9,
          1.9,
          1.8,
          1.8,
          1.9,
          1.8,
          1.9,
          1.7,
          1.9
        ],
        "per_service_mem_mib": {
          "borg": [
            1.8,
            1.7,
            1.9,
            1.9,
            1.9,
            1.8,
            1.8,
            1.9,
            1.9,
            1.8,
            1.8,
            1.9,
            1.8,
            1.9,
            1.7,
            1.9
          ]
        }
      },
      "load": {
        "per_service": {
          "borg": {
            "samples": 13,
            "mem_mean_mib": 43.15,
            "mem_max_mib": 83.04,
            "cpu_mean_pct": 55.17,
            "cpu_max_pct": 105.48
          }
        },
        "total": {
          "samples": 13,
          "dropped_for_total": 0,
          "mem_mean_mib": 43.15,
          "mem_max_mib": 83.04,
          "cpu_mean_pct": 55.17,
          "cpu_max_pct": 105.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": 22.3,
        "window": "from LAB_MARK load_start (or step start) until 2 s after the step ended",
        "reported_by_script": {
          "tool": "borg",
          "version": "1.4.5",
          "cli_startup_seconds": 0.51,
          "nproc": 2,
          "cpu_model": "AMD EPYC 9V74 80-Core Processor",
          "cpu_aes": true,
          "cpu_sha_ni": true,
          "cpu_avx2": true,
          "init": {
            "seconds": 0.64,
            "ok": true,
            "exit_code": 0,
            "repo_bytes": 43118,
            "repo_disk_bytes": 86016,
            "repo_files": 8,
            "peak_rss_mib": 82.3,
            "cpu_seconds": 0.57
          },
          "settings": "Encrypted: Yes (repokey BLAKE2b)",
          "first_backup": {
            "seconds": 3.6,
            "ok": true,
            "exit_code": 0,
            "repo_bytes": 221334332,
            "repo_disk_bytes": 221388800,
            "repo_files": 12,
            "peak_rss_mib": 82.4,
            "cpu_seconds": 1.94,
            "files": 3348,
            "source_bytes": 334222462
          },
          "first_backup_no_compression": {
            "seconds": 4.88,
            "ok": true,
            "exit_code": 0,
            "repo_bytes": 235189190,
            "repo_disk_bytes": 235245568,
            "repo_files": 12,
            "peak_rss_mib": 88.9,
            "cpu_seconds": 2.18,
            "files": 3348,
            "source_bytes": 334222462,
            "compression": "off"
          },
          "incremental": {
            "seconds": 1.18,
            "ok": true,
            "exit_code": 0,
            "repo_bytes": 241417212,
            "repo_disk_bytes": 241483776,
            "repo_files": 16,
            "peak_rss_mib": 87.0,
            "cpu_seconds": 0.92,
            "files": 3369,
            "source_bytes": 342525272,
            "changed_files": 208,
            "changed_bytes_whole_files": 64380805,
            "deleted_files": 20,
            "repo_growth_bytes": 20082880
          },
          "unchanged_rerun": {
            "seconds": 0.79,
            "ok": true,
            "exit_code": 0,
            "repo_bytes": 241418604,
            "repo_disk_bytes": 241500160,
            "repo_files": 20,
            "peak_rss_mib": 82.5,
            "cpu_seconds": 0.7,
            "files": 3369,
            "source_bytes": 342525272,
            "repo_growth_bytes": 1392
          },
          "check_default": {
            "seconds": 1.61,
            "ok": true,
            "exit_code": 0,
            "peak_rss_mib": 82.4,
            "cpu_seconds": 0.68
          },
          "check_deep": {
            "seconds": 2.34,
            "ok": true,
            "exit_code": 0,
            "peak_rss_mib": 82.4,
            "cpu_seconds": 1.42
          },
          "restore": {
            "seconds": 2.33,
            "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": 86.7,
            "cpu_seconds": 1.99
          },
          "script_seconds": 22.33,
          "all_ok": true
        },
        "output_tail": [
          "source day 1: 3348 files, 334222462 bytes, matches manifest-day1 (3348 hashes)",
          "init: 0.64s",
          "first backup: 3.60s, repository 221334332 bytes, peak RSS 82.4 MiB",
          "first backup, compression off: 4.88s, repository 235189190 bytes",
          "source day 2: 3369 files, 342525272 bytes (208 files written or replaced, 20 deleted), matches manifest-day2",
          "incremental: 1.18s (first backup 3.60s), repository grew by 20082880 bytes, peak RSS 87.0 MiB",
          "unchanged re-run: 0.79s, repository grew by 1392 bytes",
          "check: default {\"seconds\": 1.61, \"ok\": true, \"exit_code\": 0, \"peak_rss_mib\": 82.4, \"cpu_seconds\": 0.68}, deep {\"seconds\": 2.34, \"ok\": true, \"exit_code\": 0, \"peak_rss_mib\": 82.4, \"cpu_seconds\": 1.42}",
          "rm: cannot remove '/restore': Device or resource busy",
          "restore: 2.33s, 3369 of 3369 files verified by sha256, symlinks match: true, peak RSS 86.7 MiB"
        ],
        "series": [
          {
            "t_offset_s": 0.0,
            "total_mem_mib": 3.34,
            "total_cpu_pct": 105.48
          },
          {
            "t_offset_s": 2.0,
            "total_mem_mib": 53.86,
            "total_cpu_pct": 102.63
          },
          {
            "t_offset_s": 4.0,
            "total_mem_mib": 79.83,
            "total_cpu_pct": 16.86
          },
          {
            "t_offset_s": 6.0,
            "total_mem_mib": 46.62,
            "total_cpu_pct": 101.62
          },
          {
            "t_offset_s": 8.0,
            "total_mem_mib": 69.35,
            "total_cpu_pct": 64.08
          },
          {
            "t_offset_s": 10.0,
            "total_mem_mib": 62.96,
            "total_cpu_pct": 4.1
          },
          {
            "t_offset_s": 12.0,
            "total_mem_mib": 11.7,
            "total_cpu_pct": 26.08
          },
          {
            "t_offset_s": 14.0,
            "total_mem_mib": 42.66,
            "total_cpu_pct": 89.83
          },
          {
            "t_offset_s": 16.0,
            "total_mem_mib": 6.05,
            "total_cpu_pct": 43.38
          },
          {
            "t_offset_s": 18.0,
            "total_mem_mib": 64.02,
            "total_cpu_pct": 79.18
          },
          {
            "t_offset_s": 20.1,
            "total_mem_mib": 83.04,
            "total_cpu_pct": 83.96
          },
          {
            "t_offset_s": 22.1,
            "total_mem_mib": 18.8,
            "total_cpu_pct": 0.0
          },
          {
            "t_offset_s": 24.1,
            "total_mem_mib": 18.72,
            "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:32Z",
          "per_service": {
            "borg": {
              "cgroup": "v2",
              "anon_mib": 3.71,
              "file_mib": 623.85,
              "active_file_mib": 0.0,
              "inactive_file_mib": 623.85,
              "shmem_mib": 0.0
            }
          },
          "read_lag_s": 0.1,
          "series_index": 10,
          "sample_t_offset_s": 20.1,
          "total_mem_mib": 83.04
        }
      },
      "container_end_state": {
        "borg": {
          "restart_count": 0,
          "oom_killed": false
        }
      },
      "teardown": {
        "done": true,
        "volumes_removed": true,
        "images_removed": false,
        "error": null
      },
      "warnings": [],
      "finished_at": "2026-10-19T03:59:49Z",
      "elapsed_seconds": 204.5
    }
  ],
  "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: python:3.12-slim, entrypoint replaced by a short script that downloads and verifies the Borg binary, then `sleep infinity`. The container's idle RAM is that of `sleep`; its startup time includes the download (about 28 MB).",
    "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.",
    "BorgBackup 1.4.5 (stable 1.4 series, PyPI and GitHub release of 18 Jul 2026 marked Latest; Borg 2.0 is in beta, 2.0.0b25 on 27 Sep 2026, and is not tested). There is no official image: the container is python:3.12-slim (Debian 13) with the project's standalone binary borg-linux-glibc231-x86_64 from the release page, downloaded and checked against the SHA-256 shown there (c8457f70...98fb) every time the container starts. The binary unpacks itself into /tmp on every start (`cli_startup_seconds` shows the cost); a packaged Borg does not. Peak RSS counts the processes named borg (the PyInstaller launcher and the Python process). Encryption: `borg init --encryption=repokey-blake2` (AES-256 in counter mode with an HMAC-BLAKE2b authentication tag; the key is stored in the repository, locked by BORG_PASSPHRASE). The Borg 1.4 docs say repokey is as good, and prefer repokey-blake2 only on a CPU without SHA-256 hardware, so the mode is one fixed choice (`cpu_sha_ni` in the result shows which kind of CPU the run had). Compression: Borg's default, lz4 ('Compression is lz4 by default', `borg help compression` of 1.4.5). Chunker: buzhash 19,23,21,4095 (average about 2 MiB). `first_backup_no_compression` uses --compression none on a second repository.",
    "Commands (all from bench.sh): `borg init --encryption=repokey-blake2 /repo`; `borg create --stats /repo::day1 /data/src` (then ::day2, ::day2-again); `borg create --stats --compression none ALT::day1 /data/src`; `borg check /repo` and `borg check --verify-data /repo`; `cd /restore && borg extract /repo::day2-again`. Borg keeps a files cache and chunk index under ~/.cache/borg in the container (not in the repository size)."
  ],
  "warnings": [],
  "files": {
    "compose": "backup-borg.compose.yaml",
    "env": "backup-borg.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"
  }
}
