{
  "schema_version": 3,
  "app": "authelia",
  "display_name": "Authelia",
  "upstream_repo": "https://github.com/authelia/authelia",
  "docs_url": "https://www.authelia.com/integration/deployment/docker/",
  "status": "ok",
  "summary": {
    "primary_service": "authelia",
    "app_version": "4.39.28",
    "app_version_pinned": true,
    "runs": 3,
    "publishable": true,
    "publishable_note": null,
    "idle_total_mem_mib": 60.07,
    "idle_total_cpu_pct": 0.02,
    "peak_total_mem_mib": 626.3,
    "peak_is_under_load": true,
    "startup_seconds": 1.2,
    "startup_peak_total_mem_mib": 60.31,
    "settled": true,
    "settle_seconds_used": 121.2,
    "peak_max_mib": 657.0,
    "sizing_peak_mib": 657.0,
    "idle_mem_mib": {
      "median": 60.07,
      "min": 57.75,
      "max": 60.46,
      "values": [
        57.75,
        60.07,
        60.46
      ]
    },
    "idle_cpu_pct": {
      "median": 0.02,
      "min": 0.02,
      "max": 0.02,
      "values": [
        0.02,
        0.02,
        0.02
      ]
    },
    "peak_mem_mib": {
      "median": 626.3,
      "min": 497.8,
      "max": 657.0,
      "values": [
        497.8,
        626.3,
        657.0
      ]
    },
    "startup_peak_mem_mib": {
      "median": 60.31,
      "min": 57.97,
      "max": 62.61,
      "values": [
        57.97,
        60.31,
        62.61
      ]
    },
    "startup_s": {
      "median": 1.2,
      "min": 1.2,
      "max": 1.2,
      "values": [
        1.2,
        1.2,
        1.2
      ]
    },
    "settled_after_healthy_s": {
      "median": 122.7,
      "min": 122.7,
      "max": 122.7,
      "values": [
        122.7,
        122.7,
        122.7
      ]
    },
    "settled_runs": 3,
    "per_service": {
      "authelia": {
        "idle_mem_mib": {
          "median": 60.07,
          "min": 57.75,
          "max": 60.46,
          "values": [
            57.75,
            60.07,
            60.46
          ]
        },
        "peak_mem_mib": {
          "median": 626.3,
          "min": 497.8,
          "max": 657.0,
          "values": [
            497.8,
            626.3,
            657.0
          ]
        }
      }
    },
    "variance_flag": null,
    "representative_run": 2,
    "cpu_models": [
      "AMD EPYC 7763 64-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-20T04:22:16Z",
    "date": "2026-10-20",
    "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 7763 64-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": 1.2,
  "finished_at": "2026-10-20T04:30:43Z",
  "elapsed_seconds": 506.6,
  "services": {
    "authelia": {
      "image_ref": "authelia/authelia:4.39.28",
      "image_repo": "authelia/authelia",
      "image_tag": "4.39.28",
      "digest": "sha256:bd97cff4fcbf715b5ff1f9ae286afbe6033afce385302520b0368122d43a6f54",
      "digest_note": null,
      "version": "4.39.28",
      "version_source": "exec",
      "version_pinned": true,
      "version_note": "version taken from the running app (prefer_exec), not from the image label",
      "resolved_tag": "4.39.28",
      "resolved_tag_note": null,
      "image_size": {
        "unpacked_mb": 79.7,
        "unpacked_source": "docker image ls Size: unpacked image on the daemon host, rounded by docker to 3 significant figures",
        "compressed_mb": 28.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": "authelia/authelia:4.39.28",
      "compose_comment": "sha256:bd97cff4fcbf715b5ff1f9ae286afbe6033afce385302520b0368122d43a6f54  (version 4.39.28, from exec)",
      "restart_count": 0,
      "oom_killed": false
    }
  },
  "runs": [
    {
      "run": 1,
      "started_at": "2026-10-20T04:22:22Z",
      "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 7763 64-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-20",
      "startup": {
        "seconds": 1.2,
        "ok": true,
        "checks": [
          {
            "type": "http",
            "url": "http://authelia:9091/api/health",
            "expect_status": [
              200
            ],
            "ok": true,
            "ready_after_s": 1.2,
            "detail": "OK 200"
          }
        ],
        "up_command_seconds": 0.3,
        "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": {
        "authelia": "4.39.28"
      },
      "setup": [
        {
          "exit_code": 0,
          "duration_s": 0.5,
          "reported": {
            "health_http": 200,
            "forward_auth_without_session_http": 401,
            "forward_auth_without_session_location_points_at_portal": true,
            "forward_auth_unlisted_host_http": 403,
            "forward_auth_endpoint": "/api/authz/forward-auth",
            "setup_seconds": 0.0
          },
          "output_tail": [],
          "description": "Check that Authelia answers on /api/health, that a forward-auth check without a session is refused (401 with a Location at the portal) and that a host no access-control rule allows is refused (403). The script sends X-Forwarded-Proto: https because Authelia must see an https request. No password is used here: the password checks (wrong password refused, login, authorised check 200) run at the start of the load step, just before its measured window, so that the idle figure belongs to an instance nobody has logged in to (a login makes Authelia hold on to the memory of its Argon2 hash)."
        },
        {
          "exit_code": 0,
          "duration_s": 0.1,
          "reported": {
            "password_hash_format": "argon2id$v=19$m=65536,t=3,p=4"
          },
          "output_tail": [
            "stored password hash format: argon2id$v=19$m=65536,t=3,p=4"
          ],
          "description": "Read, inside the Authelia container, the Argon2 parameters of the stored password hash of the user (the \"$argon2id$v=19$m=...,t=...,p=...\" prefix only; the salt and the hash are cut off and never printed). This records which password hashing Authelia really verifies against."
        }
      ],
      "idle": {
        "per_service": {
          "authelia": {
            "samples": 16,
            "mem_mean_mib": 57.75,
            "mem_max_mib": 57.97,
            "cpu_mean_pct": 0.02,
            "cpu_max_pct": 0.04
          }
        },
        "total": {
          "samples": 16,
          "dropped_for_total": 0,
          "mem_mean_mib": 57.75,
          "mem_max_mib": 57.97,
          "cpu_mean_pct": 0.02,
          "cpu_max_pct": 0.04
        },
        "window_seconds": 30.3,
        "series": [
          {
            "t_offset_s": 0.0,
            "total_mem_mib": 57.97,
            "total_cpu_pct": 0.02
          },
          {
            "t_offset_s": 2.0,
            "total_mem_mib": 57.97,
            "total_cpu_pct": 0.02
          },
          {
            "t_offset_s": 4.0,
            "total_mem_mib": 57.78,
            "total_cpu_pct": 0.02
          },
          {
            "t_offset_s": 6.1,
            "total_mem_mib": 57.78,
            "total_cpu_pct": 0.02
          },
          {
            "t_offset_s": 8.1,
            "total_mem_mib": 57.78,
            "total_cpu_pct": 0.04
          },
          {
            "t_offset_s": 10.1,
            "total_mem_mib": 57.78,
            "total_cpu_pct": 0.02
          },
          {
            "t_offset_s": 12.1,
            "total_mem_mib": 57.78,
            "total_cpu_pct": 0.02
          },
          {
            "t_offset_s": 14.1,
            "total_mem_mib": 57.78,
            "total_cpu_pct": 0.02
          },
          {
            "t_offset_s": 16.2,
            "total_mem_mib": 57.78,
            "total_cpu_pct": 0.02
          },
          {
            "t_offset_s": 18.2,
            "total_mem_mib": 57.54,
            "total_cpu_pct": 0.04
          },
          {
            "t_offset_s": 20.2,
            "total_mem_mib": 57.54,
            "total_cpu_pct": 0.02
          },
          {
            "t_offset_s": 22.2,
            "total_mem_mib": 57.79,
            "total_cpu_pct": 0.02
          },
          {
            "t_offset_s": 24.2,
            "total_mem_mib": 57.79,
            "total_cpu_pct": 0.02
          },
          {
            "t_offset_s": 26.3,
            "total_mem_mib": 57.79,
            "total_cpu_pct": 0.02
          },
          {
            "t_offset_s": 28.3,
            "total_mem_mib": 57.54,
            "total_cpu_pct": 0.03
          },
          {
            "t_offset_s": 30.3,
            "total_mem_mib": 57.54,
            "total_cpu_pct": 0.02
          }
        ],
        "memory_stat": {
          "read_at": "2026-10-20T04:24:56Z",
          "per_service": {
            "authelia": {
              "cgroup": "v2",
              "anon_mib": 56.68,
              "file_mib": 0.3,
              "active_file_mib": 0.0,
              "inactive_file_mib": 0.3,
              "shmem_mib": 0.0
            }
          },
          "read_lag_s": 0.1,
          "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.2,
        "settle_started_after_healthy_s": 1.5,
        "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": 57.69,
          "min_mib": 57.47,
          "max_mib": 57.97,
          "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": {
          "authelia": 57.97
        },
        "total_mem_mib": 57.97,
        "samples": 61,
        "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": [
          57.6,
          57.9,
          57.8,
          57.7,
          57.6,
          57.7,
          57.7,
          57.7,
          57.6,
          57.6,
          57.7,
          57.8,
          57.9,
          57.8,
          57.7,
          57.5
        ],
        "per_service_mem_mib": {
          "authelia": [
            57.6,
            57.9,
            57.8,
            57.7,
            57.6,
            57.7,
            57.7,
            57.7,
            57.6,
            57.6,
            57.7,
            57.8,
            57.9,
            57.8,
            57.7,
            57.5
          ]
        }
      },
      "load": {
        "per_service": {
          "authelia": {
            "samples": 5,
            "mem_mean_mib": 299.62,
            "mem_max_mib": 497.8,
            "cpu_mean_pct": 112.43,
            "cpu_max_pct": 196.82
          }
        },
        "total": {
          "samples": 5,
          "dropped_for_total": 0,
          "mem_mean_mib": 299.62,
          "mem_max_mib": 497.8,
          "cpu_mean_pct": 112.43,
          "cpu_max_pct": 196.82
        },
        "description": "One user, over plain HTTP on the Compose network, from one Python script that plays the part of the reverse proxy (the proxy itself is not in the stack). First 60 password logins, 4 at a time, each from a fresh client with no cookies: POST /api/firstfactor (one request each, one Argon2id verification each). Then 3,000 authorisation checks, 8 at a time, spread evenly over the 60 sessions (50 each): GET /api/authz/forward-auth with the X-Forwarded-Method, -Proto, -Host and -URI headers and the session cookie, which must answer 200. The authentik app runs the same script with the same counts (60 logins, 4 at a time; 3,000 checks, 8 at a time). No browser, no second factor, no OpenID Connect clients, no LDAP, one user, one protected host. Just before the measured window (not counted) a pre-flight checks that a wrong password is refused, the right one logs in, an authorised check returns 200 with the user name and a check without a session is refused.",
        "duration_s": 7.1,
        "window": "from LAB_MARK load_start (or step start) until 2 s after the step ended",
        "reported_by_script": {
          "auth_load": {
            "app": "authelia",
            "logins_requested": 60,
            "logins_ok": 60,
            "login_concurrency": 4,
            "login_phase_seconds": 6.13,
            "logins_per_second": 9.79,
            "login_latency_ms": {
              "n": 60,
              "p50": 359.26,
              "p95": 738.87,
              "p99": 974.2,
              "max": 974.2,
              "mean": 402.44
            },
            "http_requests_per_login_incl_handshake": 1.0,
            "sso_handshake_requests_per_login": 0,
            "preflight_ok": true,
            "checks_requested": 3000,
            "checks_sent": 3000,
            "checks_ok": 3000,
            "check_concurrency": 8,
            "check_phase_seconds": 0.92,
            "checks_per_second": 3276.4,
            "check_latency_ms": {
              "n": 3000,
              "p50": 2.05,
              "p95": 5.12,
              "p99": 7.56,
              "max": 11.2,
              "mean": 2.42
            },
            "check_statuses": {
              "200": 3000
            },
            "requests_total": 3060,
            "requests_failed": 0,
            "login_errors": []
          },
          "logins_ok": 60,
          "checks_ok": 3000,
          "requests_total": 3060,
          "requests_failed": 0
        },
        "output_tail": [
          "authelia: 60 logins at 4 at a time, then 3000 checks at 8 at a time",
          "pre-flight ok: wrong password refused, login works, authorised check 200, check without session refused",
          "logins ok 60/60 in 6.1 s"
        ],
        "series": [
          {
            "t_offset_s": 0.0,
            "total_mem_mib": 367.2,
            "total_cpu_pct": 110.5
          },
          {
            "t_offset_s": 2.0,
            "total_mem_mib": 496.4,
            "total_cpu_pct": 196.82
          },
          {
            "t_offset_s": 4.0,
            "total_mem_mib": 497.8,
            "total_cpu_pct": 192.3
          },
          {
            "t_offset_s": 6.0,
            "total_mem_mib": 68.57,
            "total_cpu_pct": 62.49
          },
          {
            "t_offset_s": 8.0,
            "total_mem_mib": 68.12,
            "total_cpu_pct": 0.02
          }
        ],
        "series_start_offset_s": 1.5,
        "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-20T04:25:04Z",
          "per_service": {
            "authelia": {
              "cgroup": "v2",
              "anon_mib": 540.52,
              "file_mib": 0.3,
              "active_file_mib": 0.0,
              "inactive_file_mib": 0.3,
              "shmem_mib": 0.0
            }
          },
          "read_lag_s": 0.1,
          "series_index": 2,
          "sample_t_offset_s": 4.0,
          "total_mem_mib": 497.8
        }
      },
      "container_end_state": {
        "authelia": {
          "restart_count": 0,
          "oom_killed": false
        }
      },
      "teardown": {
        "done": true,
        "volumes_removed": true,
        "images_removed": false,
        "error": null
      },
      "warnings": [],
      "finished_at": "2026-10-20T04:25:08Z",
      "elapsed_seconds": 166.6
    },
    {
      "run": 2,
      "started_at": "2026-10-20T04:25:08Z",
      "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 7763 64-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-20",
      "startup": {
        "seconds": 1.2,
        "ok": true,
        "checks": [
          {
            "type": "http",
            "url": "http://authelia:9091/api/health",
            "expect_status": [
              200
            ],
            "ok": true,
            "ready_after_s": 1.2,
            "detail": "OK 200"
          }
        ],
        "up_command_seconds": 0.3,
        "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": {
        "authelia": "4.39.28"
      },
      "setup": [
        {
          "exit_code": 0,
          "duration_s": 0.5,
          "reported": {
            "health_http": 200,
            "forward_auth_without_session_http": 401,
            "forward_auth_without_session_location_points_at_portal": true,
            "forward_auth_unlisted_host_http": 403,
            "forward_auth_endpoint": "/api/authz/forward-auth",
            "setup_seconds": 0.0
          },
          "output_tail": [],
          "description": "Check that Authelia answers on /api/health, that a forward-auth check without a session is refused (401 with a Location at the portal) and that a host no access-control rule allows is refused (403). The script sends X-Forwarded-Proto: https because Authelia must see an https request. No password is used here: the password checks (wrong password refused, login, authorised check 200) run at the start of the load step, just before its measured window, so that the idle figure belongs to an instance nobody has logged in to (a login makes Authelia hold on to the memory of its Argon2 hash)."
        },
        {
          "exit_code": 0,
          "duration_s": 0.1,
          "reported": {
            "password_hash_format": "argon2id$v=19$m=65536,t=3,p=4"
          },
          "output_tail": [
            "stored password hash format: argon2id$v=19$m=65536,t=3,p=4"
          ],
          "description": "Read, inside the Authelia container, the Argon2 parameters of the stored password hash of the user (the \"$argon2id$v=19$m=...,t=...,p=...\" prefix only; the salt and the hash are cut off and never printed). This records which password hashing Authelia really verifies against."
        }
      ],
      "idle": {
        "per_service": {
          "authelia": {
            "samples": 16,
            "mem_mean_mib": 60.07,
            "mem_max_mib": 60.33,
            "cpu_mean_pct": 0.02,
            "cpu_max_pct": 0.04
          }
        },
        "total": {
          "samples": 16,
          "dropped_for_total": 0,
          "mem_mean_mib": 60.07,
          "mem_max_mib": 60.33,
          "cpu_mean_pct": 0.02,
          "cpu_max_pct": 0.04
        },
        "window_seconds": 30.3,
        "series": [
          {
            "t_offset_s": 0.0,
            "total_mem_mib": 59.83,
            "total_cpu_pct": 0.03
          },
          {
            "t_offset_s": 2.0,
            "total_mem_mib": 59.83,
            "total_cpu_pct": 0.02
          },
          {
            "t_offset_s": 4.0,
            "total_mem_mib": 60.11,
            "total_cpu_pct": 0.02
          },
          {
            "t_offset_s": 6.1,
            "total_mem_mib": 60.11,
            "total_cpu_pct": 0.02
          },
          {
            "t_offset_s": 8.1,
            "total_mem_mib": 59.92,
            "total_cpu_pct": 0.04
          },
          {
            "t_offset_s": 10.1,
            "total_mem_mib": 59.92,
            "total_cpu_pct": 0.02
          },
          {
            "t_offset_s": 12.1,
            "total_mem_mib": 59.92,
            "total_cpu_pct": 0.02
          },
          {
            "t_offset_s": 14.1,
            "total_mem_mib": 59.83,
            "total_cpu_pct": 0.02
          },
          {
            "t_offset_s": 16.2,
            "total_mem_mib": 60.08,
            "total_cpu_pct": 0.02
          },
          {
            "t_offset_s": 18.2,
            "total_mem_mib": 60.33,
            "total_cpu_pct": 0.03
          },
          {
            "t_offset_s": 20.2,
            "total_mem_mib": 60.08,
            "total_cpu_pct": 0.02
          },
          {
            "t_offset_s": 22.2,
            "total_mem_mib": 60.08,
            "total_cpu_pct": 0.02
          },
          {
            "t_offset_s": 24.2,
            "total_mem_mib": 60.33,
            "total_cpu_pct": 0.02
          },
          {
            "t_offset_s": 26.3,
            "total_mem_mib": 60.33,
            "total_cpu_pct": 0.02
          },
          {
            "t_offset_s": 28.3,
            "total_mem_mib": 60.33,
            "total_cpu_pct": 0.03
          },
          {
            "t_offset_s": 30.3,
            "total_mem_mib": 60.08,
            "total_cpu_pct": 0.02
          }
        ],
        "memory_stat": {
          "read_at": "2026-10-20T04:27:43Z",
          "per_service": {
            "authelia": {
              "cgroup": "v2",
              "anon_mib": 59.01,
              "file_mib": 0.3,
              "active_file_mib": 0.0,
              "inactive_file_mib": 0.3,
              "shmem_mib": 0.0
            }
          },
          "read_lag_s": 0.1,
          "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.2,
        "settle_started_after_healthy_s": 1.5,
        "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": 60.0,
          "min_mib": 59.81,
          "max_mib": 60.31,
          "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": {
          "authelia": 60.31
        },
        "total_mem_mib": 60.31,
        "samples": 61,
        "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": [
          59.9,
          60.0,
          60.1,
          60.2,
          60.0,
          60.2,
          60.0,
          60.0,
          59.9,
          60.1,
          60.0,
          60.1,
          60.0,
          59.9,
          60.2,
          60.2
        ],
        "per_service_mem_mib": {
          "authelia": [
            59.9,
            60.0,
            60.1,
            60.2,
            60.0,
            60.2,
            60.0,
            60.0,
            59.9,
            60.1,
            60.0,
            60.1,
            60.0,
            59.9,
            60.2,
            60.2
          ]
        }
      },
      "load": {
        "per_service": {
          "authelia": {
            "samples": 5,
            "mem_mean_mib": 570.76,
            "mem_max_mib": 626.3,
            "cpu_mean_pct": 109.01,
            "cpu_max_pct": 196.54
          }
        },
        "total": {
          "samples": 5,
          "dropped_for_total": 0,
          "mem_mean_mib": 570.76,
          "mem_max_mib": 626.3,
          "cpu_mean_pct": 109.01,
          "cpu_max_pct": 196.54
        },
        "description": "One user, over plain HTTP on the Compose network, from one Python script that plays the part of the reverse proxy (the proxy itself is not in the stack). First 60 password logins, 4 at a time, each from a fresh client with no cookies: POST /api/firstfactor (one request each, one Argon2id verification each). Then 3,000 authorisation checks, 8 at a time, spread evenly over the 60 sessions (50 each): GET /api/authz/forward-auth with the X-Forwarded-Method, -Proto, -Host and -URI headers and the session cookie, which must answer 200. The authentik app runs the same script with the same counts (60 logins, 4 at a time; 3,000 checks, 8 at a time). No browser, no second factor, no OpenID Connect clients, no LDAP, one user, one protected host. Just before the measured window (not counted) a pre-flight checks that a wrong password is refused, the right one logs in, an authorised check returns 200 with the user name and a check without a session is refused.",
        "duration_s": 7.2,
        "window": "from LAB_MARK load_start (or step start) until 2 s after the step ended",
        "reported_by_script": {
          "auth_load": {
            "app": "authelia",
            "logins_requested": 60,
            "logins_ok": 60,
            "login_concurrency": 4,
            "login_phase_seconds": 6.21,
            "logins_per_second": 9.66,
            "login_latency_ms": {
              "n": 60,
              "p50": 357.35,
              "p95": 839.89,
              "p99": 955.85,
              "max": 955.85,
              "mean": 405.89
            },
            "http_requests_per_login_incl_handshake": 1.0,
            "sso_handshake_requests_per_login": 0,
            "preflight_ok": true,
            "checks_requested": 3000,
            "checks_sent": 3000,
            "checks_ok": 3000,
            "check_concurrency": 8,
            "check_phase_seconds": 0.91,
            "checks_per_second": 3290.1,
            "check_latency_ms": {
              "n": 3000,
              "p50": 2.05,
              "p95": 5.21,
              "p99": 7.16,
              "max": 14.18,
              "mean": 2.41
            },
            "check_statuses": {
              "200": 3000
            },
            "requests_total": 3060,
            "requests_failed": 0,
            "login_errors": []
          },
          "logins_ok": 60,
          "checks_ok": 3000,
          "requests_total": 3060,
          "requests_failed": 0
        },
        "output_tail": [
          "authelia: 60 logins at 4 at a time, then 3000 checks at 8 at a time",
          "pre-flight ok: wrong password refused, login works, authorised check 200, check without session refused",
          "logins ok 60/60 in 6.2 s"
        ],
        "series": [
          {
            "t_offset_s": 0.0,
            "total_mem_mib": 425.5,
            "total_cpu_pct": 82.01
          },
          {
            "t_offset_s": 2.0,
            "total_mem_mib": 625.3,
            "total_cpu_pct": 196.54
          },
          {
            "t_offset_s": 4.0,
            "total_mem_mib": 626.3,
            "total_cpu_pct": 193.33
          },
          {
            "t_offset_s": 6.0,
            "total_mem_mib": 587.8,
            "total_cpu_pct": 73.13
          },
          {
            "t_offset_s": 8.0,
            "total_mem_mib": 588.9,
            "total_cpu_pct": 0.02
          }
        ],
        "series_start_offset_s": 1.4,
        "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-20T04:27:51Z",
          "per_service": {
            "authelia": {
              "cgroup": "v2",
              "anon_mib": 480.09,
              "file_mib": 0.38,
              "active_file_mib": 0.08,
              "inactive_file_mib": 0.3,
              "shmem_mib": 0.0
            }
          },
          "read_lag_s": 0.1,
          "series_index": 2,
          "sample_t_offset_s": 4.0,
          "total_mem_mib": 626.3
        }
      },
      "container_end_state": {
        "authelia": {
          "restart_count": 0,
          "oom_killed": false
        }
      },
      "teardown": {
        "done": true,
        "volumes_removed": true,
        "images_removed": false,
        "error": null
      },
      "warnings": [],
      "finished_at": "2026-10-20T04:27:56Z",
      "elapsed_seconds": 167.1
    },
    {
      "run": 3,
      "started_at": "2026-10-20T04:27:56Z",
      "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 7763 64-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-20",
      "startup": {
        "seconds": 1.2,
        "ok": true,
        "checks": [
          {
            "type": "http",
            "url": "http://authelia:9091/api/health",
            "expect_status": [
              200
            ],
            "ok": true,
            "ready_after_s": 1.2,
            "detail": "OK 200"
          }
        ],
        "up_command_seconds": 0.3,
        "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": {
        "authelia": "4.39.28"
      },
      "setup": [
        {
          "exit_code": 0,
          "duration_s": 0.5,
          "reported": {
            "health_http": 200,
            "forward_auth_without_session_http": 401,
            "forward_auth_without_session_location_points_at_portal": true,
            "forward_auth_unlisted_host_http": 403,
            "forward_auth_endpoint": "/api/authz/forward-auth",
            "setup_seconds": 0.0
          },
          "output_tail": [],
          "description": "Check that Authelia answers on /api/health, that a forward-auth check without a session is refused (401 with a Location at the portal) and that a host no access-control rule allows is refused (403). The script sends X-Forwarded-Proto: https because Authelia must see an https request. No password is used here: the password checks (wrong password refused, login, authorised check 200) run at the start of the load step, just before its measured window, so that the idle figure belongs to an instance nobody has logged in to (a login makes Authelia hold on to the memory of its Argon2 hash)."
        },
        {
          "exit_code": 0,
          "duration_s": 0.1,
          "reported": {
            "password_hash_format": "argon2id$v=19$m=65536,t=3,p=4"
          },
          "output_tail": [
            "stored password hash format: argon2id$v=19$m=65536,t=3,p=4"
          ],
          "description": "Read, inside the Authelia container, the Argon2 parameters of the stored password hash of the user (the \"$argon2id$v=19$m=...,t=...,p=...\" prefix only; the salt and the hash are cut off and never printed). This records which password hashing Authelia really verifies against."
        }
      ],
      "idle": {
        "per_service": {
          "authelia": {
            "samples": 16,
            "mem_mean_mib": 60.46,
            "mem_max_mib": 60.6,
            "cpu_mean_pct": 0.02,
            "cpu_max_pct": 0.04
          }
        },
        "total": {
          "samples": 16,
          "dropped_for_total": 0,
          "mem_mean_mib": 60.46,
          "mem_max_mib": 60.6,
          "cpu_mean_pct": 0.02,
          "cpu_max_pct": 0.04
        },
        "window_seconds": 30.3,
        "series": [
          {
            "t_offset_s": 0.0,
            "total_mem_mib": 60.59,
            "total_cpu_pct": 0.02
          },
          {
            "t_offset_s": 2.0,
            "total_mem_mib": 60.34,
            "total_cpu_pct": 0.03
          },
          {
            "t_offset_s": 4.0,
            "total_mem_mib": 60.1,
            "total_cpu_pct": 0.02
          },
          {
            "t_offset_s": 6.1,
            "total_mem_mib": 60.35,
            "total_cpu_pct": 0.02
          },
          {
            "t_offset_s": 8.1,
            "total_mem_mib": 60.35,
            "total_cpu_pct": 0.03
          },
          {
            "t_offset_s": 10.1,
            "total_mem_mib": 60.35,
            "total_cpu_pct": 0.02
          },
          {
            "t_offset_s": 12.1,
            "total_mem_mib": 60.35,
            "total_cpu_pct": 0.02
          },
          {
            "t_offset_s": 14.1,
            "total_mem_mib": 60.35,
            "total_cpu_pct": 0.02
          },
          {
            "t_offset_s": 16.2,
            "total_mem_mib": 60.35,
            "total_cpu_pct": 0.02
          },
          {
            "t_offset_s": 18.2,
            "total_mem_mib": 60.6,
            "total_cpu_pct": 0.03
          },
          {
            "t_offset_s": 20.2,
            "total_mem_mib": 60.6,
            "total_cpu_pct": 0.03
          },
          {
            "t_offset_s": 22.2,
            "total_mem_mib": 60.6,
            "total_cpu_pct": 0.03
          },
          {
            "t_offset_s": 24.2,
            "total_mem_mib": 60.6,
            "total_cpu_pct": 0.02
          },
          {
            "t_offset_s": 26.3,
            "total_mem_mib": 60.6,
            "total_cpu_pct": 0.02
          },
          {
            "t_offset_s": 28.3,
            "total_mem_mib": 60.6,
            "total_cpu_pct": 0.04
          },
          {
            "t_offset_s": 30.3,
            "total_mem_mib": 60.6,
            "total_cpu_pct": 0.02
          }
        ],
        "memory_stat": {
          "read_at": "2026-10-20T04:30:30Z",
          "per_service": {
            "authelia": {
              "cgroup": "v2",
              "anon_mib": 56.02,
              "file_mib": 0.3,
              "active_file_mib": 0.0,
              "inactive_file_mib": 0.3,
              "shmem_mib": 0.0
            }
          },
          "read_lag_s": 0.1,
          "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.2,
        "settle_started_after_healthy_s": 1.5,
        "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": 62.12,
          "min_mib": 60.59,
          "max_mib": 62.37,
          "spread_mib": 1.78,
          "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": {
          "authelia": 62.61
        },
        "total_mem_mib": 62.61,
        "samples": 61,
        "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": [
          62.4,
          62.3,
          62.3,
          62.2,
          62.1,
          62.2,
          62.1,
          62.3,
          62.2,
          62.2,
          62.2,
          62.3,
          60.4,
          60.4,
          60.6,
          60.6
        ],
        "per_service_mem_mib": {
          "authelia": [
            62.4,
            62.3,
            62.3,
            62.2,
            62.1,
            62.2,
            62.1,
            62.3,
            62.2,
            62.2,
            62.2,
            62.3,
            60.4,
            60.4,
            60.6,
            60.6
          ]
        }
      },
      "load": {
        "per_service": {
          "authelia": {
            "samples": 5,
            "mem_mean_mib": 548.92,
            "mem_max_mib": 657.0,
            "cpu_mean_pct": 111.48,
            "cpu_max_pct": 196.51
          }
        },
        "total": {
          "samples": 5,
          "dropped_for_total": 0,
          "mem_mean_mib": 548.92,
          "mem_max_mib": 657.0,
          "cpu_mean_pct": 111.48,
          "cpu_max_pct": 196.51
        },
        "description": "One user, over plain HTTP on the Compose network, from one Python script that plays the part of the reverse proxy (the proxy itself is not in the stack). First 60 password logins, 4 at a time, each from a fresh client with no cookies: POST /api/firstfactor (one request each, one Argon2id verification each). Then 3,000 authorisation checks, 8 at a time, spread evenly over the 60 sessions (50 each): GET /api/authz/forward-auth with the X-Forwarded-Method, -Proto, -Host and -URI headers and the session cookie, which must answer 200. The authentik app runs the same script with the same counts (60 logins, 4 at a time; 3,000 checks, 8 at a time). No browser, no second factor, no OpenID Connect clients, no LDAP, one user, one protected host. Just before the measured window (not counted) a pre-flight checks that a wrong password is refused, the right one logs in, an authorised check returns 200 with the user name and a check without a session is refused.",
        "duration_s": 7.2,
        "window": "from LAB_MARK load_start (or step start) until 2 s after the step ended",
        "reported_by_script": {
          "auth_load": {
            "app": "authelia",
            "logins_requested": 60,
            "logins_ok": 60,
            "login_concurrency": 4,
            "login_phase_seconds": 6.16,
            "logins_per_second": 9.75,
            "login_latency_ms": {
              "n": 60,
              "p50": 360.6,
              "p95": 799.31,
              "p99": 962.27,
              "max": 962.27,
              "mean": 404.36
            },
            "http_requests_per_login_incl_handshake": 1.0,
            "sso_handshake_requests_per_login": 0,
            "preflight_ok": true,
            "checks_requested": 3000,
            "checks_sent": 3000,
            "checks_ok": 3000,
            "check_concurrency": 8,
            "check_phase_seconds": 0.91,
            "checks_per_second": 3302.4,
            "check_latency_ms": {
              "n": 3000,
              "p50": 2.06,
              "p95": 5.13,
              "p99": 7.0,
              "max": 12.01,
              "mean": 2.4
            },
            "check_statuses": {
              "200": 3000
            },
            "requests_total": 3060,
            "requests_failed": 0,
            "login_errors": []
          },
          "logins_ok": 60,
          "checks_ok": 3000,
          "requests_total": 3060,
          "requests_failed": 0
        },
        "output_tail": [
          "authelia: 60 logins at 4 at a time, then 3000 checks at 8 at a time",
          "pre-flight ok: wrong password refused, login works, authorised check 200, check without session refused",
          "logins ok 60/60 in 6.2 s"
        ],
        "series": [
          {
            "t_offset_s": 0.0,
            "total_mem_mib": 362.6,
            "total_cpu_pct": 84.7
          },
          {
            "t_offset_s": 2.0,
            "total_mem_mib": 549.1,
            "total_cpu_pct": 196.51
          },
          {
            "t_offset_s": 4.0,
            "total_mem_mib": 519.1,
            "total_cpu_pct": 193.65
          },
          {
            "t_offset_s": 6.0,
            "total_mem_mib": 657.0,
            "total_cpu_pct": 82.54
          },
          {
            "t_offset_s": 8.0,
            "total_mem_mib": 656.8,
            "total_cpu_pct": 0.02
          }
        ],
        "series_start_offset_s": 1.4,
        "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-20T04:30:40Z",
          "per_service": {
            "authelia": {
              "cgroup": "v2",
              "anon_mib": 640.94,
              "file_mib": 0.31,
              "active_file_mib": 0.0,
              "inactive_file_mib": 0.31,
              "shmem_mib": 0.0
            }
          },
          "read_lag_s": 0.1,
          "series_index": 3,
          "sample_t_offset_s": 6.0,
          "total_mem_mib": 657.0
        }
      },
      "container_end_state": {
        "authelia": {
          "restart_count": 0,
          "oom_killed": false
        }
      },
      "teardown": {
        "done": true,
        "volumes_removed": true,
        "images_removed": false,
        "error": null
      },
      "warnings": [],
      "finished_at": "2026-10-20T04:30:43Z",
      "elapsed_seconds": 167.1
    }
  ],
  "load_defined": true,
  "notes": [
    "Redis: not used and not in the stack. The session provider is Authelia's default in-memory one. The Authelia docs (https://www.authelia.com/configuration/session/redis/) recommend Redis for high availability and production; they do not say it is required for one instance. Sessions are therefore lost when the container restarts (users log in again).",
    "Minimal configuration for one small instance: users in a YAML file, SQLite storage, the file notifier (password-reset codes are written to /config/notification.txt instead of e-mailed). The docs say the file backend and SQLite prevent running more than one instance.",
    "Authelia must be served over HTTPS, 'not optional even for testing' (https://www.authelia.com/integration/prologue/get-started/). The lab has no TLS and no proxy: the script sends X-Forwarded-Proto: https. The domain is example.com (portal auth.example.com, protected site app.example.com); a real deployment needs a real domain and a reverse proxy that does TLS.",
    "Authelia checks the clock against an NTP server at start and logs a warning if it cannot reach one (it did not matter for the lab; the check can take a few seconds on a host without outbound UDP/123).",
    "Password hashing: Argon2id with Authelia's defaults (variant argon2id, iterations 3, memory 65536 KiB = 64 MiB, parallelism 4, key length 32, salt length 16; https://www.authelia.com/configuration/first-factor/file/, also what `authelia crypto hash generate argon2` uses by default in 4.39.28). The lab user's stored hash was made with those defaults and the setup step reads the parameters back from the stored hash. Every login runs one hash, so concurrent logins multiply the 64 MiB. Authelia's own guidance (https://www.authelia.com/reference/guides/passwords/) lists 'low memory' (t=3, m=65536, p=4) and a 'recommended' profile (t=1, m=2097152 KiB = 2 GiB, p=4) and aims at about 500 ms per hash.",
    "Same load as the authentik app: 60 password logins, 4 at a time, then 3,000 authorisation checks, 8 at a time, spread over the 60 sessions (50 each); the counts are in the test.yaml of both apps and a test fails if they differ. A pre-flight of a few requests (one wrong password, one login, two checks) runs just before the measured window and is not counted. It is there and not in the setup step because a password login makes Authelia hold on to its Argon2 memory: with two logins in the setup step the idle figure was 160 MiB, without them 62 MiB (two 1-run development runs).",
    "NOT equivalent, on purpose and recorded: one Authelia login is one request (POST /api/firstfactor) and its session cookie is valid for the forward-auth check directly; one authentik login is about 6 requests plus a 3-request single sign-on handshake with the embedded outpost. The two apps need very different time for the same counts, so Authelia's load window is seconds (the peak rests on a handful of 2-second samples; the memory stays at the peak after the hashes finish) and authentik's is minutes.",
    "The three secrets (JWT secret, session secret, storage encryption key) are lab-only values in .env; the published .env has them and the password hash commented out."
  ],
  "warnings": [],
  "files": {
    "compose": "authelia.compose.yaml",
    "env": "authelia.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"
  }
}
