This repository measures two things for several deployment tools:
- Packaging: how long a tool takes to turn Lambda functions into what it uploads, and how big that upload is. For containers, only the image size.
- Deploying a change: how long it takes from starting a deploy until the changed code answers requests.
The tools are Stacktape, Serverless Framework, SST, AWS CDK, Render, Heroku and Ravion. Stacktape publishes these results on its website, so we wrote down every choice we made. If you think a tool is set up worse than its users would set it up, please open an issue.
Every test has its own branch, named <app>/<tool>, for example
lambda-25/sst. A branch holds only that
application and that tool's configuration, so it deploys as it is. Its README gives the tool version, the setup, the
commands and the shape of the result. The list of branches is below.
You need Node.js 22 or newer, npm, Git, Docker with BuildKit, zip, the AWS CLI and a disposable AWS account.
The AWS deployments run in eu-west-1. To measure how long SST takes to deploy a change to the 25-function Lambda backend:
git clone https://github.com/stacktape/deploy-benchmarks && cd deploy-benchmarks
npm ci
export AWS_PROFILE=<profile of a disposable account>
node bench.mjs setup sst lambda-25 # write the lambda-25/sst branch to work/ and deploy it once (not measured)
node bench.mjs deploy sst lambda-25 --runs 10 # deploy a code change ten times
node report.mjs # median, fastest and slowest run
node bench.mjs remove sst lambda-25 # delete everything setup createdReplace sst lambda-25 with any tool and application from the list of branches. The other measurements are
node bench.mjs package (packaging and image size) and node bench.mjs push (Git push), described in
Run the benchmark. Each tool needs these credentials:
| Tool | What it needs |
|---|---|
| Stacktape | AWS_PROFILE: a profile for the disposable AWS account. The harness uses it to call the deployed code and read stack outputs.STACKTAPE_API_KEY, or run stacktape login once.STACKTAPE_AWS_ACCOUNT: the name of the AWS account connection in the Stacktape Console. Stacktape deploys with credentials from that connection, not with AWS_PROFILE, so check with stacktape info:whoami that both point to the same account.STACKTAPE_BIN: the path of the Stacktape binary to measure. |
| Serverless Framework | AWS_PROFILE: a profile for the disposable AWS account.SERVERLESS_ACCESS_KEY, or run npm exec --workspace=packages/functions -- serverless login once. |
| SST | AWS_PROFILE: a profile for the disposable AWS account. |
| AWS CDK | AWS_PROFILE: a profile for the disposable AWS account. cdk bootstrap runs once during setup. |
| AWS CDK with hotswap | AWS_PROFILE: a profile for the disposable AWS account. cdk bootstrap runs once during setup. |
| Render | RENDER_API_KEY: the harness creates the services, reads their URLs and build events and empties their build caches through the Render API.BENCH_FORK: the URL of your fork. Render's GitHub app must have access to it. |
| Heroku | HEROKU_API_KEY, or run heroku login once.The builds plugin: heroku plugins:install @heroku-cli/heroku-builds. |
| Ravion | Run ravion login once.AWS credentials for the connected account, for example AWS_PROFILE: the harness empties the build cache in ECR and reads image sizes from ECR.RAVION_URL_API, and for five services also RAVION_URL_REPORTING, RAVION_URL_INVENTORY, RAVION_URL_WEBHOOKS and RAVION_URL_NOTIFICATIONS: the URL of each service. |
These are the results of a qualification run on 17 October 2026, between 13:47 and 20:37 UTC. It checked the
harness on a dedicated machine and measured the Stacktape fixes merged up to
d51f56ad. The figures on
stacktape.com will come from a rerun with the released Stacktape CLI (Stacktape's launch item L20), so these numbers
can change before then.
- Machine: an EC2
c7i.2xlarge(8 vCPUs, 16 GB) ineu-west-1, Amazon Linux 2023, Node.js 24.15.0, Docker 29.7.2 with the containerd image store and buildx 0.38. Each result records the machine. - Stacktape: CLI
4.0.0-bench.d51f56ad, built from source as described in Build Stacktape from source. - Other tools: Serverless Framework 4.44.0, SST 4.17.2, CDK CLI 2.1145.0 and Heroku CLI 11.12.0, as pinned in
configs/. Render and Heroku built with what they offered that day. - Harness: this repository from commit
b10797fon. The fixes made during the run are listed under Changes during the run. - Order: packaging ran alone on the machine. The deploy tests then ran in rounds, one run of each tool per round, with up to four series at a time (Lambda, and one per container application); the Git push pairs ran next to them. The machine's 5-minute load average stayed below 3 during the deploy tests.
- Raw data: every run is in
results/.node report.mjsprints every series; its full output is at the end of this section.
Unless a cell gives n, it is the median of at least 10 runs, with the fastest and slowest run in brackets.
Stacktape Git-push samples have smaller counts, shown explicitly below. Times are in seconds.
Bold is the fastest or smallest in its row. A blank cell means the tool has no such test (see
What we measured). [n failed] counts runs that failed and are left out of the median.
Not measured yet:
- Stacktape Git push remains unmeasured for
lambda-1,container-pythonandfive-services. The three measured apps use a separate source-built CLI and test runner image, as described below. - Ravion is pending: our Ravion account needs a setup step from its owner.
- Ravion five-services is blocked by a Ravion bug: its EC2 builder applies
source_base_pathtwice (see Ravion).
Packaging time in seconds, empty build cache: median (fastest–slowest)
| App | Stacktape | Serverless | SST | AWS CDK |
|---|---|---|---|---|
lambda-1 |
1.7 (1.7–1.7) | 5.2 (5.0–5.5) | 6.3 (6.2–6.5) | 3.0 (2.9–3.3) |
lambda-5 |
1.9 (1.8–1.9) | 7.0 (6.9–7.2) | 7.6 (7.5–8.0) | 4.4 (4.3–4.5) |
lambda-25 |
3.2 (3.1–3.2) | 19.5 (19.2–19.8) | 14.0 (13.7–14.3) | 11.4 (11.3–11.5) |
Packaging time in seconds, after a source change: median (fastest–slowest)
| App | Stacktape | Serverless | SST | AWS CDK |
|---|---|---|---|---|
lambda-1 |
1.7 (1.7–1.7) | 5.1 (5.0–5.3) | 6.4 (6.2–6.5) | 3.0 (2.9–3.0) |
lambda-5 |
1.9 (1.8–1.9) | 7.0 (6.9–7.2) | 7.8 (7.6–8.1) | 4.4 (4.3–4.4) |
lambda-25 |
3.2 (3.1–3.3) | 19.6 (19.3–20.4) | 14.7 (14.4–15.0) | 11.4 (11.3–11.6) |
Upload size in MB (functions, layers and source maps)
| App | Stacktape | Serverless | SST | AWS CDK |
|---|---|---|---|---|
lambda-1 |
6.6 | 16.5 | 33.7 | 9.4 |
lambda-5 |
6.7 | 74.6 | 115.8 | 19.3 |
lambda-25 |
6.7 | 361.8 | 515.5 | 68.1 |
Container image size in MB (compressed, one measurement)
| App | Stacktape | SST | AWS CDK |
|---|---|---|---|
container-node |
63.3 | 86.6 | 86.6 |
container-python |
53.4 | 54.7 | 54.7 |
Deploy from the CLI: seconds until the new code serves
| App | Stacktape | Serverless | SST | AWS CDK | CDK hotswap | Heroku |
|---|---|---|---|---|---|---|
lambda-1 |
9.5 (8.8–11.1) | 19.8 (18.5–21.3) | 13.8 (13.3–20.3) | 20.7 (20.3–22.1) | 11.4 (10.9–12.7) | |
lambda-5 |
9.8 (7.2–10.5) | 23.1 (22.6–24.7) | 14.9 (14.1–15.2) | 22.4 (22.1–23.7) | 13.4 (12.7–14.5) | |
lambda-25 |
11.9 (10.5–12.3) | 48.0 (45.9–66.3) | 32.2 (25.4–33.3) | 58.7 (38.9–66.9) | 27.1 (26.0–28.4) | |
container-node |
40.1 (32.1–50.1) | 138.6 (49.0–168.1) | 68.1 (60.0–78.1) | 49.5 (36.0–64.1) | 28.0 (25.0–30.1) | |
container-python |
47.6 (39.1–65.1) | 96.6 (44.0–428.2) [1 failed] | 66.1 (61.0–85.1) | 40.5 (36.0–58.1) | 21.6 (19.1–47.1) | |
five-services |
57.6 (38.1–63.1) | 73.0 (54.0–408.2) [1 failed] | 77.6 (72.1–86.1) | 54.5 (46.0–58.0) | 30.5 (28.0–64.1) |
Deploy from the CLI, shared-package change (every function): seconds until the new code serves
| App | Stacktape | Serverless | SST | AWS CDK | CDK hotswap | Heroku |
|---|---|---|---|---|---|---|
lambda-1 |
9.5 (9.1–9.9) | 19.7 (19.2–20.6) | 13.6 (13.0–16.7) [2 failed] | 20.8 (20.2–21.3) | 11.6 (11.4–12.4) | |
lambda-5 |
29.9 (28.9–31.1) | 23.1 (22.3–108.2) | 14.8 (14.5–15.8) | 22.6 (22.2–23.5) | 13.2 (12.8–13.5) | |
lambda-25 |
37.7 (34.3–114.4) | 46.7 (43.3–50.6) | 31.7 (29.3–33.2) | 58.4 (38.6–68.6) | 25.8 (24.8–27.9) |
Git push with an empty build cache: seconds from git push until the new code serves
| App | Stacktape | Serverless | SST | AWS CDK | Render | Heroku | Ravion |
|---|---|---|---|---|---|---|---|
lambda-1 |
pending | 57.4 (47.1–61.5) | 57.2 (49.8–64.3) | 58.9 (53.7–66.1) | |||
lambda-5 |
226.9 (220.5–231.9), n=4 | 65.0 (58.0–93.7) | 59.2 (53.9–63.4) | 59.9 (51.9–69.0) | |||
lambda-25 |
220.7 (125.8–240.4), n=4 | 127.8 (109.6–219.4) | 72.0 (58.9–86.1) | 102.8 (82.3–165.2) | |||
container-node |
254.2 (166.2–266.2), n=5 | 417.2 (93.1–424.2) | 108.1 (100.1–119.1) | 34.1 (28.1–116.1) | 37.5 (34.0–42.0) | pending | |
container-python |
pending | 410.7 (83.1–430.2) | 101.1 (94.0–106.1) | 54.1 (45.1–74.1) | 33.6 (29.1–38.1) | pending | |
five-services |
pending | 273.1 (125.1–414.2) | 117.0 (111.0–126.0) | 37.6 (33.1–120.1) [1 failed] | blocked |
Git push 15 minutes later: seconds from git push until the new code serves
| App | Stacktape | Serverless | SST | AWS CDK | Render | Heroku | Ravion |
|---|---|---|---|---|---|---|---|
lambda-1 |
pending | 55.4 (47.2–60.9) | 58.1 (48.5–65.4) | 55.5 (49.9–63.4) | |||
lambda-5 |
30.9 (30.4–35.2), n=3 | 64.4 (55.6–99.4) | 60.4 (45.3–66.6) | 59.8 (49.9–69.8) | |||
lambda-25 |
33.2 (32.9–34.7), n=3 | 118.4 (110.3–139.9) | 75.4 (62.7–84.4) | 105.0 (68.2–134.7) | |||
container-node |
56.1 (50.1–60.1), n=4 | 97.5 (86.0–116.1) | 109.6 (97.0–122.1) | 39.6 (31.1–70.1) | 37.0 (33.0–76.1) | pending | |
container-python |
pending | 96.1 (86.0–115.1) | 105.1 (96.0–116.1) | 55.1 (36.1–88.1) | 29.0 (27.0–34.0) | pending | |
five-services |
pending | 115.0 (106.0–134.1) | 112.1 (100.0–130.0) | 38.6 (34.1–85.1) | blocked |
Reading the results:
- Deploy time is time until the new code serves, not until the command exits. Each run also records the
command's own duration as
commandMs. Stacktape's default web service (an HTTP API in front of the container, with no health check) serves the new code after about 40 seconds, butstacktape deployexits only after about 185 seconds, when ECS has finished the rolling deployment (mediancommandMs: 184.5 s forcontainer-node, 194.2 s forcontainer-python, 203.1 s forfive-services). SST does the opposite:sst deploystarts the ECS update and exits after about 8 seconds, before the new task serves. - Stacktape's shared change on
lambda-5andlambda-25goes through a CloudFormation update. A change to the shared package changes Stacktape's shared chunk layer, which its direct code update does not handle yet, so these runs take 30–38 seconds instead of about 10. - SST's failed runs happened between 14:56 and 14:58 UTC, when the machine's
/tmpwas full. SST's Pulumi engine leaves a 22 MBpulumi-asset-*file in/tmpon every deploy, and we deleted the old files during the run. The runs are kept, and make-up runs brought each series to 10. - Render's failed run on
five-services: thewebhooksservice returned HTTP 404 during the first run, about ten minutes after the service had been recreated. - Render deployed from a second copy of the fork. Render's GitHub app had no access to our fork, so the Render services deployed the same branches and commits from another repository the app could see.
These separate qualification samples ran on 17–18 October 2026 through the dev Console, with workloads in
eu-west-1. They use CLI commit 16d16c8ed97e95c8129f684cbf143341da29bbf5, version
4.0.0-bench.16d16c8e, baked into test AMI ami-01572ab71483bcc1e (build bmk-push-36c3460d). No npm publication
was needed. Other tools' numbers and the CLI-only Stacktape results above retain their original revisions and machines.
The observer was WSL on an i7-13700K; builds ran on a c7a.2xlarge runner with a persistent cache disk in the
connected AWS account, not on the observer or the other qualification run's c7i.2xlarge.
A push to the project's connected GitHub branch triggers the Console runner. Cold means its instance, cache disk and database runner record were removed before the push. Warm is the paired push 15 minutes after readiness; the logs confirm that every included warm sample reused a running instance. The idle timer starts after the job finishes, which can be much later than readiness for containers. The separate resume samples waited for the runner to become stopped through idle hibernation before pushing; they reuse its disk and caches.
Only rows with at >= 2026-10-17T22:00Z count. Rounds 1–10 used a WSL monotonic clock that ran approximately 9% fast,
inflating elapsed readiness by roughly 12–22 seconds on cold pushes. All 60 rows remain in
results/deploy-stacktape-push.jsonl, marked with an excluded reason, and
are omitted from medians and phase summaries. The surviving rerun stopped before reaching ten samples; it contains
4 cold/3 warm Lambda samples per app and 5 cold/4 warm container samples. No additional rounds were collected.
Stacktape Git push: seconds until new code serves, median (min–max), valid n
| App | Cold | Warm, still running | Resume after hibernation |
|---|---|---|---|
lambda-5 |
226.9 (220.5–231.9), n=4 | 30.9 (30.4–35.2), n=3 | 50.3 (48.4–51.7), n=3 |
lambda-25 |
220.7 (125.8–240.4), n=4 | 33.2 (32.9–34.7), n=3 | 53.9 (51.2–54.9), n=3 |
container-node |
254.2 (166.2–266.2), n=5 | 56.1 (50.1–60.1), n=4 | 85.1 (69.2–93.1), n=3 |
The nine resume samples are preserved verbatim in
results-extra/resume.jsonl. Their collector saved readiness and runner state,
but did not persist the main harness's businessOk/unchangedOk flags; they are reported separately rather than
silently treated as fully checked deploy rows. Cold and warm rows all passed those two checks.
Runner phase breakdown, cold samples: median seconds (min–max)
CloudWatch timestamps provide the phase boundaries, with about one-second log resolution. The last column combines the readiness observer with the runner timestamp. Medians of individual phases need not sum to the median total.
| App | n | Push → Console queue | Queue → runner ready | Ready → job | Job → install | Install → CLI | CLI → serving |
|---|---|---|---|---|---|---|---|
lambda-5 |
4 | 4.9 (3.7–15.3) | 173.5 (156.1–180.5) | 2.3 (2.2–2.3) | 21.0 (21.0–21.0) | 8.0 (8.0–8.0) | 17.4 (16.5–17.9) |
lambda-25 |
4 | 7.2 (4.7–16.8) | 158.3 (64.4–185.2) | 2.2 (2.2–5.3) | 21.0 (21.0–23.0) | 8.0 (8.0–8.0) | 18.9 (18.5–19.4) |
container-node |
5 | 3.8 (3.6–8.9) | 157.7 (68.7–180.5) | 2.2 (1.9–2.2) | 21.0 (21.0–22.0) | 2.0 (2.0–2.0) | 51.6 (45.9–68.3) |
Warm queue-to-ready medians are 0.5 s; hibernation-resume medians are about 19–21 s. Warm job-to-install is 3 s,
versus 21 s cold. Lambda dependency installation takes 8 s cold and 5 s warm before the CLI starts. Cold CLI
Build & Package medians are 3.2 s (lambda-5), 4.8 s (lambda-25) and 11.7 s (container-node); warm medians are
1.9, 4.9 and 4.6 s. Container CLI completion takes about 207 s, although new code serves about 45–52 s after CLI start.
That ECS settling time is not added to readiness.
The largest optimization candidates, based on these samples and the earlier agents' log/code diagnosis, are:
- Fix runner IAM propagation and SSM retry startup. Eleven of the 13 valid cold samples took 161–190 s from push to runner-ready, versus 71–72 s for the two fast samples. Earlier log diagnosis attributes about 95 s of the slow path to a fresh instance profile reaching SSM late, backing off, and an instance restart. Avoiding that retry is the largest cold-start opportunity; this is an estimate, not a measured optimized implementation.
- Keep runner tools ready without loading unused images. Cold job-to-install is 21 s versus 3 s warm. Earlier
diagnosis found a silent roughly 20 s
docker loadof the preseeded ~480 MB Railpack archive before checkout, even for Lambda jobs. Lazy or image-ready tool preparation could recover roughly 18–20 s; checkout itself was ~1 s. - Remove the duplicate Lambda dependency install. The runner installs dependencies, then the CLI's workspace detection installs them again during Initialize (~6.8–7.3 s). Reusing the completed install could save roughly 6 s per Lambda push. This needs implementation and fresh qualification before claiming a speedup.
These are prerelease qualification figures. Before using them as launch claims, rerun with the published CLI and release runner image, verify the observer clock rate against AWS timestamps, and disclose actual running/stopped state at the 15-minute push. The temporary test image and projects are removed after this qualification.
The harness was fixed three times while the series ran. Runs that a harness bug made wrong are not deleted: they are
in results/superseded.jsonl, each with the reason in superseded, and a make-up run or
rerun replaced each of them.
- CDK's warm upload size counted the asset directories of earlier synths left in
cdk.out(61 MB instead of 9.4 MB forlambda-1). Fixed in3bde156; the CDK warm packaging series was rerun (30 runs). - The business check called each service once and could reach an old task during a rolling update. Fixed in
7e9ce35, which gives a changed service up to a minute to answer from the new revision; the clock was not changed. 21 SST and Render runs before the fix failed only this check. - Git push order: the harness could push while the previous GitHub Actions deploy was still waiting for ECS, so
CDK's next workflow found its stack in
UPDATE_IN_PROGRESSand timed out. Fixed in954bfb4, which waits for the branch's previous workflow; two CDK container runs are superseded.
Full output of node report.mjs --markdown
Lambda packaging time and upload size
| series | runs | failed | seconds: median (min–max) | MB: median (min–max) |
|---|---|---|---|---|
| lambda-1 cdk clean | 10 | 0 | 3.0 (2.9–3.3) | 9.40 (9.40–9.40) |
| lambda-1 cdk warm | 10 | 0 | 3.0 (2.9–3.0) | 9.40 (9.40–9.40) |
| lambda-1 serverless clean | 10 | 0 | 5.2 (5.0–5.5) | 16.54 (16.54–16.54) |
| lambda-1 serverless warm | 10 | 0 | 5.1 (5.0–5.3) | 16.54 (16.54–16.54) |
| lambda-1 sst clean | 10 | 0 | 6.3 (6.2–6.5) | 33.67 (33.67–33.67) |
| lambda-1 sst warm | 10 | 0 | 6.4 (6.2–6.5) | 33.67 (33.67–33.67) |
| lambda-1 stacktape clean | 10 | 0 | 1.7 (1.7–1.7) | 6.64 (6.64–6.64) |
| lambda-1 stacktape warm | 10 | 0 | 1.7 (1.7–1.7) | 6.64 (6.64–6.64) |
| lambda-25 cdk clean | 10 | 0 | 11.4 (11.3–11.5) | 68.12 (68.12–68.12) |
| lambda-25 cdk warm | 10 | 0 | 11.4 (11.3–11.6) | 68.13 (68.13–68.13) |
| lambda-25 serverless clean | 10 | 0 | 19.5 (19.2–19.8) | 361.80 (361.80–361.80) |
| lambda-25 serverless warm | 10 | 0 | 19.6 (19.3–20.4) | 361.80 (361.80–361.80) |
| lambda-25 sst clean | 10 | 0 | 14.0 (13.7–14.3) | 515.47 (515.47–515.47) |
| lambda-25 sst warm | 10 | 0 | 14.7 (14.4–15.0) | 515.48 (515.48–515.48) |
| lambda-25 stacktape clean | 10 | 0 | 3.2 (3.1–3.2) | 6.73 (6.73–6.73) |
| lambda-25 stacktape warm | 10 | 0 | 3.2 (3.1–3.3) | 6.73 (6.73–6.73) |
| lambda-5 cdk clean | 10 | 0 | 4.4 (4.3–4.5) | 19.33 (19.33–19.33) |
| lambda-5 cdk warm | 10 | 0 | 4.4 (4.3–4.4) | 19.33 (19.33–19.33) |
| lambda-5 serverless clean | 10 | 0 | 7.0 (6.9–7.2) | 74.61 (74.61–74.61) |
| lambda-5 serverless warm | 10 | 0 | 7.0 (6.9–7.2) | 74.61 (74.61–74.61) |
| lambda-5 sst clean | 10 | 0 | 7.6 (7.5–8.0) | 115.81 (115.81–115.81) |
| lambda-5 sst warm | 10 | 0 | 7.8 (7.6–8.1) | 115.81 (115.81–115.81) |
| lambda-5 stacktape clean | 10 | 0 | 1.9 (1.8–1.9) | 6.71 (6.71–6.71) |
| lambda-5 stacktape warm | 10 | 0 | 1.9 (1.8–1.9) | 6.71 (6.71–6.71) |
Container image size
| series | runs | MB: median (min–max) |
|---|---|---|
| container-node cdk | 1 | 86.60 (86.60–86.60) |
| container-node sst | 1 | 86.60 (86.60–86.60) |
| container-node stacktape | 1 | 63.35 (63.35–63.35) |
| container-python cdk | 1 | 54.69 (54.69–54.69) |
| container-python sst | 1 | 54.69 (54.69–54.69) |
| container-python stacktape | 1 | 53.44 (53.44–53.44) |
Time from deploy start (or git push return) until the new code serves
| series | runs | valid | excluded | seconds: median (min–max) |
|---|---|---|---|---|
| container-node cdk cli handlers | 10 | 10 | 0 | 68.1 (60.0–78.1) |
| container-node cdk git-cold handlers | 10 | 10 | 0 | 108.1 (100.1–119.1) |
| container-node cdk git-warm handlers | 10 | 10 | 0 | 109.6 (97.0–122.1) |
| container-node cdk-hotswap cli handlers | 10 | 10 | 0 | 49.5 (36.0–64.1) |
| container-node heroku cli handlers | 10 | 10 | 0 | 28.0 (25.0–30.1) |
| container-node heroku git-cold handlers | 10 | 10 | 0 | 37.5 (34.0–42.0) |
| container-node heroku git-warm handlers | 10 | 10 | 0 | 37.0 (33.0–76.1) |
| container-node render git-cold handlers | 12 | 12 | 0 | 34.1 (28.1–116.1) |
| container-node render git-warm handlers | 10 | 10 | 0 | 39.6 (31.1–70.1) |
| container-node sst cli handlers | 10 | 10 | 0 | 138.6 (49.0–168.1) |
| container-node sst git-cold handlers | 10 | 10 | 0 | 417.2 (93.1–424.2) |
| container-node sst git-warm handlers | 10 | 10 | 0 | 97.5 (86.0–116.1) |
| container-node stacktape cli handlers | 10 | 10 | 0 | 40.1 (32.1–50.1) |
| container-node stacktape git-cold handlers | 15 | 5 | 10 | 254.2 (166.2–266.2) |
| container-node stacktape git-warm handlers | 14 | 4 | 10 | 56.1 (50.1–60.1) |
| container-python cdk cli handlers | 10 | 10 | 0 | 66.1 (61.0–85.1) |
| container-python cdk git-cold handlers | 10 | 10 | 0 | 101.1 (94.0–106.1) |
| container-python cdk git-warm handlers | 10 | 10 | 0 | 105.1 (96.0–116.1) |
| container-python cdk-hotswap cli handlers | 10 | 10 | 0 | 40.5 (36.0–58.1) |
| container-python heroku cli handlers | 10 | 10 | 0 | 21.6 (19.1–47.1) |
| container-python heroku git-cold handlers | 10 | 10 | 0 | 33.6 (29.1–38.1) |
| container-python heroku git-warm handlers | 10 | 10 | 0 | 29.0 (27.0–34.0) |
| container-python render git-cold handlers | 11 | 11 | 0 | 54.1 (45.1–74.1) |
| container-python render git-warm handlers | 11 | 11 | 0 | 55.1 (36.1–88.1) |
| container-python sst cli handlers | 11 | 10 | 1 | 96.6 (44.0–428.2) |
| container-python sst git-cold handlers | 10 | 10 | 0 | 410.7 (83.1–430.2) |
| container-python sst git-warm handlers | 10 | 10 | 0 | 96.1 (86.0–115.1) |
| container-python stacktape cli handlers | 10 | 10 | 0 | 47.6 (39.1–65.1) |
| five-services cdk cli handlers | 10 | 10 | 0 | 77.6 (72.1–86.1) |
| five-services cdk git-cold handlers | 10 | 10 | 0 | 117.0 (111.0–126.0) |
| five-services cdk git-warm handlers | 10 | 10 | 0 | 112.1 (100.0–130.0) |
| five-services cdk-hotswap cli handlers | 10 | 10 | 0 | 54.5 (46.0–58.0) |
| five-services heroku cli handlers | 10 | 10 | 0 | 30.5 (28.0–64.1) |
| five-services render git-cold handlers | 11 | 10 | 1 | 37.6 (33.1–120.1) |
| five-services render git-warm handlers | 10 | 10 | 0 | 38.6 (34.1–85.1) |
| five-services sst cli handlers | 11 | 10 | 1 | 73.0 (54.0–408.2) |
| five-services sst git-cold handlers | 10 | 10 | 0 | 273.1 (125.1–414.2) |
| five-services sst git-warm handlers | 10 | 10 | 0 | 115.0 (106.0–134.1) |
| five-services stacktape cli handlers | 10 | 10 | 0 | 57.6 (38.1–63.1) |
| lambda-1 cdk cli handlers | 10 | 10 | 0 | 20.7 (20.3–22.1) |
| lambda-1 cdk cli shared | 10 | 10 | 0 | 20.8 (20.2–21.3) |
| lambda-1 cdk git-cold handlers | 10 | 10 | 0 | 58.9 (53.7–66.1) |
| lambda-1 cdk git-warm handlers | 10 | 10 | 0 | 55.5 (49.9–63.4) |
| lambda-1 cdk-hotswap cli handlers | 10 | 10 | 0 | 11.4 (10.9–12.7) |
| lambda-1 cdk-hotswap cli shared | 10 | 10 | 0 | 11.6 (11.4–12.4) |
| lambda-1 serverless cli handlers | 10 | 10 | 0 | 19.8 (18.5–21.3) |
| lambda-1 serverless cli shared | 10 | 10 | 0 | 19.7 (19.2–20.6) |
| lambda-1 serverless git-cold handlers | 10 | 10 | 0 | 57.4 (47.1–61.5) |
| lambda-1 serverless git-warm handlers | 10 | 10 | 0 | 55.4 (47.2–60.9) |
| lambda-1 sst cli handlers | 10 | 10 | 0 | 13.8 (13.3–20.3) |
| lambda-1 sst cli shared | 12 | 10 | 2 | 13.6 (13.0–16.7) |
| lambda-1 sst git-cold handlers | 10 | 10 | 0 | 57.2 (49.8–64.3) |
| lambda-1 sst git-warm handlers | 10 | 10 | 0 | 58.1 (48.5–65.4) |
| lambda-1 stacktape cli handlers | 10 | 10 | 0 | 9.5 (8.8–11.1) |
| lambda-1 stacktape cli shared | 10 | 10 | 0 | 9.5 (9.1–9.9) |
| lambda-25 cdk cli handlers | 10 | 10 | 0 | 58.7 (38.9–66.9) |
| lambda-25 cdk cli shared | 10 | 10 | 0 | 58.4 (38.6–68.6) |
| lambda-25 cdk git-cold handlers | 10 | 10 | 0 | 102.8 (82.3–165.2) |
| lambda-25 cdk git-warm handlers | 10 | 10 | 0 | 105.0 (68.2–134.7) |
| lambda-25 cdk-hotswap cli handlers | 10 | 10 | 0 | 27.1 (26.0–28.4) |
| lambda-25 cdk-hotswap cli shared | 10 | 10 | 0 | 25.8 (24.8–27.9) |
| lambda-25 serverless cli handlers | 10 | 10 | 0 | 48.0 (45.9–66.3) |
| lambda-25 serverless cli shared | 10 | 10 | 0 | 46.7 (43.3–50.6) |
| lambda-25 serverless git-cold handlers | 10 | 10 | 0 | 127.8 (109.6–219.4) |
| lambda-25 serverless git-warm handlers | 10 | 10 | 0 | 118.4 (110.3–139.9) |
| lambda-25 sst cli handlers | 10 | 10 | 0 | 32.2 (25.4–33.3) |
| lambda-25 sst cli shared | 10 | 10 | 0 | 31.7 (29.3–33.2) |
| lambda-25 sst git-cold handlers | 10 | 10 | 0 | 72.0 (58.9–86.1) |
| lambda-25 sst git-warm handlers | 10 | 10 | 0 | 75.4 (62.7–84.4) |
| lambda-25 stacktape cli handlers | 10 | 10 | 0 | 11.9 (10.5–12.3) |
| lambda-25 stacktape cli shared | 10 | 10 | 0 | 37.7 (34.3–114.4) |
| lambda-25 stacktape git-cold handlers | 14 | 4 | 10 | 220.7 (125.8–240.4) |
| lambda-25 stacktape git-warm handlers | 13 | 3 | 10 | 33.2 (32.9–34.7) |
| lambda-5 cdk cli handlers | 10 | 10 | 0 | 22.4 (22.1–23.7) |
| lambda-5 cdk cli shared | 10 | 10 | 0 | 22.6 (22.2–23.5) |
| lambda-5 cdk git-cold handlers | 10 | 10 | 0 | 59.9 (51.9–69.0) |
| lambda-5 cdk git-warm handlers | 10 | 10 | 0 | 59.8 (49.9–69.8) |
| lambda-5 cdk-hotswap cli handlers | 10 | 10 | 0 | 13.4 (12.7–14.5) |
| lambda-5 cdk-hotswap cli shared | 10 | 10 | 0 | 13.2 (12.8–13.5) |
| lambda-5 serverless cli handlers | 10 | 10 | 0 | 23.1 (22.6–24.7) |
| lambda-5 serverless cli shared | 10 | 10 | 0 | 23.1 (22.3–108.2) |
| lambda-5 serverless git-cold handlers | 10 | 10 | 0 | 65.0 (58.0–93.7) |
| lambda-5 serverless git-warm handlers | 10 | 10 | 0 | 64.4 (55.6–99.4) |
| lambda-5 sst cli handlers | 10 | 10 | 0 | 14.9 (14.1–15.2) |
| lambda-5 sst cli shared | 10 | 10 | 0 | 14.8 (14.5–15.8) |
| lambda-5 sst git-cold handlers | 10 | 10 | 0 | 59.2 (53.9–63.4) |
| lambda-5 sst git-warm handlers | 10 | 10 | 0 | 60.4 (45.3–66.6) |
| lambda-5 stacktape cli handlers | 10 | 10 | 0 | 9.8 (7.2–10.5) |
| lambda-5 stacktape cli shared | 10 | 10 | 0 | 29.9 (28.9–31.1) |
| lambda-5 stacktape git-cold handlers | 14 | 4 | 10 | 226.9 (220.5–231.9) |
| lambda-5 stacktape git-warm handlers | 13 | 3 | 10 | 30.9 (30.4–35.2) |
- Excluded: lambda-5 stacktape git-cold handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-lambda-5-push-2026-10-17T16-19-24-688Z.log
- Excluded: lambda-25 stacktape git-cold handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-lambda-25-push-2026-10-17T16-19-24-683Z.log
- Excluded: container-node stacktape git-cold handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-container-node-push-2026-10-17T16-19-24-687Z.log
- Excluded: lambda-5 stacktape git-warm handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-lambda-5-push-2026-10-17T16-19-24-688Z.log
- Excluded: lambda-25 stacktape git-warm handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-lambda-25-push-2026-10-17T16-19-24-683Z.log
- Excluded: container-node stacktape git-warm handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-container-node-push-2026-10-17T16-19-24-687Z.log
- Excluded: lambda-5 stacktape git-cold handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-lambda-5-push-2026-10-17T16-43-30-053Z.log
- Excluded: lambda-25 stacktape git-cold handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-lambda-25-push-2026-10-17T16-43-30-057Z.log
- Excluded: container-node stacktape git-cold handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-container-node-push-2026-10-17T16-43-30-055Z.log
- Excluded: lambda-5 stacktape git-warm handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-lambda-5-push-2026-10-17T16-43-30-053Z.log
- Excluded: lambda-25 stacktape git-warm handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-lambda-25-push-2026-10-17T16-43-30-057Z.log
- Excluded: container-node stacktape git-warm handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-container-node-push-2026-10-17T16-43-30-055Z.log
- Excluded: lambda-25 stacktape git-cold handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-lambda-25-push-2026-10-17T17-07-53-633Z.log
- Excluded: lambda-5 stacktape git-cold handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-lambda-5-push-2026-10-17T17-07-53-633Z.log
- Excluded: container-node stacktape git-cold handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-container-node-push-2026-10-17T17-07-53-634Z.log
- Excluded: lambda-5 stacktape git-warm handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-lambda-5-push-2026-10-17T17-07-53-633Z.log
- Excluded: lambda-25 stacktape git-warm handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-lambda-25-push-2026-10-17T17-07-53-633Z.log
- Excluded: container-node stacktape git-warm handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-container-node-push-2026-10-17T17-07-53-634Z.log
- Excluded: lambda-25 stacktape git-cold handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-lambda-25-push-2026-10-17T17-31-33-927Z.log
- Excluded: lambda-5 stacktape git-cold handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-lambda-5-push-2026-10-17T17-31-33-937Z.log
- Excluded: container-node stacktape git-cold handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-container-node-push-2026-10-17T17-31-33-926Z.log
- Excluded: lambda-25 stacktape git-warm handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-lambda-25-push-2026-10-17T17-31-33-927Z.log
- Excluded: lambda-5 stacktape git-warm handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-lambda-5-push-2026-10-17T17-31-33-937Z.log
- Excluded: container-node stacktape git-warm handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-container-node-push-2026-10-17T17-31-33-926Z.log
- Excluded: lambda-5 stacktape git-cold handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-lambda-5-push-2026-10-17T17-54-32-207Z.log
- Excluded: lambda-25 stacktape git-cold handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-lambda-25-push-2026-10-17T17-54-32-208Z.log
- Excluded: container-node stacktape git-cold handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-container-node-push-2026-10-17T17-54-32-206Z.log
- Excluded: lambda-5 stacktape git-warm handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-lambda-5-push-2026-10-17T17-54-32-207Z.log
- Excluded: lambda-25 stacktape git-warm handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-lambda-25-push-2026-10-17T17-54-32-208Z.log
- Excluded: container-node stacktape git-warm handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-container-node-push-2026-10-17T17-54-32-206Z.log
- Excluded: lambda-5 stacktape git-cold handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-lambda-5-push-2026-10-17T18-18-19-841Z.log
- Excluded: lambda-25 stacktape git-cold handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-lambda-25-push-2026-10-17T18-18-19-849Z.log
- Excluded: container-node stacktape git-cold handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-container-node-push-2026-10-17T18-18-19-841Z.log
- Excluded: lambda-25 stacktape git-warm handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-lambda-25-push-2026-10-17T18-18-19-849Z.log
- Excluded: lambda-5 stacktape git-warm handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-lambda-5-push-2026-10-17T18-18-19-841Z.log
- Excluded: container-node stacktape git-warm handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-container-node-push-2026-10-17T18-18-19-841Z.log
- Excluded: lambda-25 stacktape git-cold handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-lambda-25-push-2026-10-17T18-41-02-340Z.log
- Excluded: lambda-5 stacktape git-cold handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-lambda-5-push-2026-10-17T18-41-02-340Z.log
- Excluded: container-node stacktape git-cold handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-container-node-push-2026-10-17T18-41-02-340Z.log
- Excluded: lambda-5 stacktape git-warm handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-lambda-5-push-2026-10-17T18-41-02-340Z.log
- Excluded: lambda-25 stacktape git-warm handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-lambda-25-push-2026-10-17T18-41-02-340Z.log
- Excluded: container-node stacktape git-warm handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-container-node-push-2026-10-17T18-41-02-340Z.log
- Excluded: lambda-25 stacktape git-cold handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-lambda-25-push-2026-10-17T19-05-15-706Z.log
- Excluded: lambda-5 stacktape git-cold handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-lambda-5-push-2026-10-17T19-05-15-705Z.log
- Excluded: container-node stacktape git-cold handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-container-node-push-2026-10-17T19-05-15-707Z.log
- Excluded: lambda-25 stacktape git-warm handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-lambda-25-push-2026-10-17T19-05-15-706Z.log
- Excluded: lambda-5 stacktape git-warm handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-lambda-5-push-2026-10-17T19-05-15-705Z.log
- Excluded: container-node stacktape git-warm handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-container-node-push-2026-10-17T19-05-15-707Z.log
- Excluded: lambda-5 stacktape git-cold handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-lambda-5-push-2026-10-17T19-28-09-441Z.log
- Excluded: lambda-25 stacktape git-cold handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-lambda-25-push-2026-10-17T19-28-09-435Z.log
- Excluded: container-node stacktape git-cold handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-container-node-push-2026-10-17T19-28-09-434Z.log
- Excluded: lambda-5 stacktape git-warm handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-lambda-5-push-2026-10-17T19-28-09-441Z.log
- Excluded: lambda-25 stacktape git-warm handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-lambda-25-push-2026-10-17T19-28-09-435Z.log
- Excluded: container-node stacktape git-warm handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-container-node-push-2026-10-17T19-28-09-434Z.log
- Excluded: lambda-5 stacktape git-cold handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-lambda-5-push-2026-10-17T19-51-59-376Z.log
- Excluded: lambda-25 stacktape git-cold handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-lambda-25-push-2026-10-17T19-51-59-369Z.log
- Excluded: container-node stacktape git-cold handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-container-node-push-2026-10-17T19-51-59-353Z.log
- Excluded: lambda-5 stacktape git-warm handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-lambda-5-push-2026-10-17T19-51-59-376Z.log
- Excluded: lambda-25 stacktape git-warm handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-lambda-25-push-2026-10-17T19-51-59-369Z.log
- Excluded: container-node stacktape git-warm handlers run 1 (excluded: Rounds 1–10: WSL monotonic clock ran approximately 9% fast; readiness timings invalid. Valid series starts 2026-10-17T22:00Z.); log logs/bmk-stacktape-container-node-push-2026-10-17T19-51-59-353Z.log
- Excluded: lambda-1 sst cli shared run 1 (failed: Stopped waiting: the deploy command failed); log logs/bmk-sst-lambda-1-deploy-2026-10-17T14-56-45-510Z.log
- Excluded: five-services sst cli handlers run 1 (failed: Stopped waiting: the deploy command failed); log logs/bmk-sst-five-services-deploy-2026-10-17T14-57-54-158Z.log
- Excluded: container-python sst cli handlers run 1 (failed: Stopped waiting: the deploy command failed); log logs/bmk-sst-container-python-deploy-2026-10-17T14-57-56-426Z.log
- Excluded: lambda-1 sst cli shared run 1 (failed: Stopped waiting: the deploy command failed); log logs/bmk-sst-lambda-1-deploy-2026-10-17T14-58-12-043Z.log
- Excluded: five-services render git-cold handlers run 1 (failed: webhooks: HTTP 404); log logs/bmk-render-five-services-push-2026-10-17T15-00-22-358Z.log
Stacktape Git push after verified hibernation (separate samples)
| series | runs | seconds: median (min–max) |
|---|---|---|
| container-node stacktape git-resume | 3 | 85.1 (69.2–93.1) |
| lambda-25 stacktape git-resume | 3 | 53.9 (51.2–54.9) |
| lambda-5 stacktape git-resume | 3 | 50.3 (48.4–51.7) |
| Branch | Application | Tool | Measured |
|---|---|---|---|
lambda-1/stacktape |
lambda-1 |
Stacktape | packaging, deploy from a laptop, Git push |
lambda-5/stacktape |
lambda-5 |
Stacktape | packaging, deploy from a laptop, Git push |
lambda-25/stacktape |
lambda-25 |
Stacktape | packaging, deploy from a laptop, Git push |
container-node/stacktape |
container-node |
Stacktape | image size, deploy from a laptop, Git push |
container-python/stacktape |
container-python |
Stacktape | image size, deploy from a laptop, Git push |
five-services/stacktape |
five-services |
Stacktape | deploy from a laptop, Git push |
lambda-1/serverless |
lambda-1 |
Serverless Framework | packaging, deploy from a laptop, Git push |
lambda-5/serverless |
lambda-5 |
Serverless Framework | packaging, deploy from a laptop, Git push |
lambda-25/serverless |
lambda-25 |
Serverless Framework | packaging, deploy from a laptop, Git push |
lambda-1/sst |
lambda-1 |
SST | packaging, deploy from a laptop, Git push |
lambda-5/sst |
lambda-5 |
SST | packaging, deploy from a laptop, Git push |
lambda-25/sst |
lambda-25 |
SST | packaging, deploy from a laptop, Git push |
container-node/sst |
container-node |
SST | image size, deploy from a laptop, Git push |
container-python/sst |
container-python |
SST | image size, deploy from a laptop, Git push |
five-services/sst |
five-services |
SST | deploy from a laptop, Git push |
lambda-1/cdk |
lambda-1 |
AWS CDK | packaging, deploy from a laptop, Git push |
lambda-5/cdk |
lambda-5 |
AWS CDK | packaging, deploy from a laptop, Git push |
lambda-25/cdk |
lambda-25 |
AWS CDK | packaging, deploy from a laptop, Git push |
container-node/cdk |
container-node |
AWS CDK | image size, deploy from a laptop, Git push |
container-python/cdk |
container-python |
AWS CDK | image size, deploy from a laptop, Git push |
five-services/cdk |
five-services |
AWS CDK | deploy from a laptop, Git push |
lambda-1/cdk-hotswap |
lambda-1 |
AWS CDK with hotswap | packaging, deploy from a laptop |
lambda-5/cdk-hotswap |
lambda-5 |
AWS CDK with hotswap | packaging, deploy from a laptop |
lambda-25/cdk-hotswap |
lambda-25 |
AWS CDK with hotswap | packaging, deploy from a laptop |
container-node/cdk-hotswap |
container-node |
AWS CDK with hotswap | image size, deploy from a laptop |
container-python/cdk-hotswap |
container-python |
AWS CDK with hotswap | image size, deploy from a laptop |
five-services/cdk-hotswap |
five-services |
AWS CDK with hotswap | deploy from a laptop |
container-node/render |
container-node |
Render | Git push |
container-python/render |
container-python |
Render | Git push |
five-services/render |
five-services |
Render | Git push |
container-node/heroku |
container-node |
Heroku | deploy from a laptop, Git push |
container-python/heroku |
container-python |
Heroku | deploy from a laptop, Git push |
five-services/heroku |
five-services |
Heroku | deploy from a laptop |
container-node/ravion |
container-node |
Ravion | Git push |
container-python/ravion |
container-python |
Ravion | Git push |
five-services/ravion |
five-services |
Ravion | Git push |
The Lambda applications are the backend of a small online shop, built like many real Lambda backends: an npm
workspace whose functions all import a shared service package (Prisma with PostgreSQL, the AWS SDK, Stripe, Slack,
Google Sheets, OpenAI, email) and a shared util package. Most functions are queue workers and scheduled jobs; four
are HTTP APIs; one builds a PDF, an Excel workbook and a Word document. The 1- and 5-function applications are
subsets of the 25-function one. The container applications are small commerce APIs whose code does real work. All
of them are described in fixtures/README.md.
| Test | Applications | Tools |
|---|---|---|
| Lambda packaging | lambda-1, lambda-5, lambda-25 |
Stacktape, Serverless Framework, SST, AWS CDK |
| Container image size | container-node, container-python |
Stacktape, SST, AWS CDK |
| Deploy a change from a laptop | all of the above, plus five-services |
the tools that deploy from the command line |
| Deploy a change by Git push, first push | all of the above | every tool |
| Deploy a change by Git push, 15 minutes later | same as the first push | every tool |
In the five-service application, the change edits three services. The other two must keep running their old code.
The Lambda applications have a second deploy test, the shared change: it edits a module of the shared service
package, which every function imports, so every function must be rebuilt.
The change. Every function and service has a small revision file, for example
create-order.revision.ts. A deploy run
writes a new revision string into the revision file of every changed function or service. Every response from the
applications contains the revision of the code that produced it. All tools get the same change. The shared change
(--change shared) instead edits serviceRevision in
packages/service/src/orders.ts, which every Lambda response
reports.
When a deploy counts as done. The clock starts when the deploy command starts. For a Git push, it starts when
git push returns. The clock stops when every changed function or service has returned the new revision. Functions
are invoked every 250 ms and services are called every second. The clock does not wait for the command to exit; the
command's own duration is recorded separately. After the clock stops, every function and service must also return
the right business result (for example a cart total of 33.00) from the expected revision. During a rolling update
a load balancer still sends some requests to the old code, so a changed service gets up to a minute to answer from
the new revision; the clock has already stopped. Runs that fail this check are kept in the raw data but left out of
the results.
Packaging. The clock covers the tool's own packaging command. Before each run we delete the tool's previous build output. Every series starts with one run that is not measured, so nothing a tool downloads on first use is timed. The "warm" series keeps the previous build and changes only the source, like a developer's next build.
Container build time is part of the deploy, not packaging. No tool builds a container image except as part of a deploy, so the deploy test times each tool's whole deploy command, image build included; for containers, the packaging test reports only the image size.
Upload size. For Lambda, this is everything the tool uploads as code: each function's ZIP, each layer it creates, and the source maps it uploads next to the ZIPs (SST and Stacktape do this). For containers, it is the compressed image size reported by the local Docker image store, which is what a push to an empty registry uploads. Heroku reports a compressed slug instead of an image. Render does not report a size.
Not measured. Creating the infrastructure the first time, installing tools and dependencies, and the checks after each run.
Recorded for every run: the tool and its version, the Stacktape commit when Stacktape is built from source, the machine, the timings, the sizes and a log of every command.
Every tool uses its documented defaults, except where noted in Where a tool needed help. On AWS, every function uses Node.js 22 with 512 MB of memory and a 30-second timeout, and every container uses 0.25 vCPU and 512 MB on Fargate.
Regions and instance sizes. The AWS tools deploy to eu-west-1 (Ireland), and the measuring machine runs there
too. Heroku apps run in Heroku's EU region, which is also in Ireland, and Render services in Frankfurt, Render's
closest region. Each Heroku app runs on one Basic dyno (512 MB, always on) and each Render service on one Starter
instance (0.5 CPU, 512 MB). These are the smallest always-on sizes of each platform, comparable to the 0.25 vCPU,
512 MB Fargate tasks. The npm tools are pinned in each project's package.json and package-lock.json. Render,
Heroku and Ravion choose their own build images, so they run whatever version the platform offers at the time of the
measurement. The exact files are in the test branches and in configs/; node configs.mjs regenerates
them from the tool modules in tools/.
- Configuration:
configs/stacktape/, written bytools/stacktape.mjs. - Version: the CLI built from source at a commit that each result records. See Build Stacktape from source.
- Packaging:
stacktape package. Deploy:stacktape deploy. - Functions and the Node.js services use Stacktape's
js-bundlepackaging. The Python service usesbuildpack. stacktape deployupdates code-only changes directly, without a CloudFormation update. This is its default.- Git push: Stacktape Console's Git integration, which deploys on the project's runner in your AWS account (a c7a.2xlarge EC2 instance with a cache disk). Stacktape Git push runs describes what the first push and the push 15 minutes later find on the runner.
- Configuration:
configs/serverless/, written bytools/serverless.mjs. - Version: 4.44.0.
- Packaging:
serverless package. Deploy:serverless deploy. - The Serverless service is the monorepo's
packages/functionspackage, where itsserverless.ymllives. That is the usual layout, and Serverless installsexternalpackages at the versions in that package'spackage.json. - Version 4 with its built-in esbuild bundling (
build.esbuild), with esbuild's default options. Without that setting, version 4 bundles only TypeScript handlers. We turned it on because most Serverless projects bundle their code. In the last month, theserverlesspackage had 5.9 million downloads,serverless-esbuild2.0 million,serverless-webpack1.0 million andserverless-plugin-typescript0.5 million. Weekly, version 3 had 681,000 downloads and version 4 had 511,000 (October 2026). serverless deploy functionis faster for a single function, but it is a separate command and skips CloudFormation, so we useserverless deploy.- Git push: GitHub Actions, using the shared workflow. Serverless Framework v4 has no Git deployment service of its own.
- Configuration:
configs/sst/, written bytools/sst.mjs. - Version: 4.17.2.
- Deploy:
sst deploy. Functions usesst.aws.Function; services usesst.aws.Serviceon an SST cluster. - Packaging: SST has no command that only packages.
sst diffon a deployed stage builds and zips every function, then compares the plan with what is deployed. Its time therefore includes that comparison, which reads from AWS. - Containers are built from a plain Dockerfile: the official slim image, a
locked production install, then the source. SST runs
docker buildduringsst deploy; for the image size, the packaging test runs the samedocker builddirectly. - Git push: the shared GitHub Actions workflow. SST's own Autodeploy needs an SST Console account and is not measured.
- Configuration:
configs/cdk/, written bytools/cdk.mjs. - Version: CDK CLI 2.1145.0 with aws-cdk-lib 2.273.0, constructs 10.8.1 and esbuild 0.28.2.
- Deploy:
cdk deploy. Functions useNodejsFunction; services useApplicationLoadBalancedFargateService. - Packaging:
cdk synth, which bundles each function with esbuild (installed in the project, as CDK recommends). CDK zips the bundles later, duringcdk deploy, so zipping is not part of CDK's packaging time. For the upload size we zip each bundle at the same compression level CDK uses. - Containers use the same Dockerfile as SST, and the image size comes from the same direct
docker build. - Git push: the shared GitHub Actions workflow.
- CDK with hotswap is reported as a separate tool, with its own branches (
<app>/cdk-hotswap).cdk deploy --hotswapupdates Lambda code and ECS services directly, which is CDK's fast path for code changes. CDK documents it for development stacks only, because it leaves the CloudFormation stack out of date. Its first deploy is a normalcdk deploy. It has no Git push test.
- Configuration:
configs/render/, written bytools/render.mjs. - Render's native Node.js and Python runtimes on Starter instances in Frankfurt, with the build and start commands from Render's Express and FastAPI guides.
- Render deploys only from a Git repository, so it has no laptop deploy. Build time comes from Render's build events.
node bench.mjs setupcreates the services ofrender.yamlthrough the Render API. The first push of each pair starts with "Clear build cache & deploy" of the previous commit, through the API, and waits until it is live.
tools/heroku.mjs. No extra configuration: the applications already have aProcfileand runtime version files.- The default stack and the official Node.js and Python buildpacks. Each service is its own Heroku app, in the EU region, on one Basic dyno.
- Laptop deploy:
git pushto Heroku's Git remote. Git push: Heroku's GitHub integration with automatic deploys. - Heroku builds the repository root. For the five-service application we push each service with
git subtree push, and its GitHub integration cannot deploy it.
tools/ravion.mjs. Ravion's configuration lives in the Ravion project, not in the test branch. Railpack detects the runtime, dependencies and start command from the source.- Ravion deploys only from a Git repository, so it has no laptop deploy. Build time and image size come from the build step of Ravion's pipeline runs and from ECR.
- One Ravion project (
bmk) with one environment (bench) ineu-west-1, set up as in Ravion's Node.js guide with the current module versions:rvn-aws-network1.2.1 with its defaults (three subnet pairs, one NAT gateway).rvn-ecs-cluster2.2.0 withuse_ravion_managed_domains: trueandload_balancer_deletion_protection_enabled: false.- One
rvn-ecs-web2.3.0 per service:build_source: railpack,source_repo: github.com/<you>/deploy-benchmarks,container_port: 3000,health_check_path: /health,fargate_size: {vcpu: 0.25, memory_gb: 0.5}andbuild_capacity_type: ec2-spot. The module and service names arecontainer-node/bmk-container-node,container-python/bmk-container-python, and for five servicesfive-services-<service>/bmk-fs-<service>withsource_base_path: <service>. Every other input keeps its default, including Railpack's commands, autoscaling from one task and rolling deploys. - One pipeline per service, with the module's given ID: a
buildstep withbranchandreffrom the push, then adeploystep with the build'simage_digest, and awebhook:githubpush trigger filtered to the test's branch (and, for five services,paths: ['<service>/**']). - The project's Terraform change and destroy pipelines are copies of Ravion's system pipelines with
infrastructure: {type: ec2, instance_size: c7a.large}instead of sandboxes. They only create the infrastructure, which is not measured.
- The five-service application does not build on Ravion yet. Ravion's EC2 builder applies
source_base_pathtwice (directory .../api/api does not exist), so a service in a subdirectory cannot be built with Railpack.
| What | Tools | Why |
|---|---|---|
Keep pdfkit out of the bundle and install it as a package: build.esbuild.external (Serverless), nodejs.install (SST), bundling.nodeModules (CDK) |
Serverless Framework, SST, AWS CDK | PDFKit reads its font files from disk. Bundled, the sales report fails with ENOENT ... Helvetica.afm. We checked that each of these tools fails without the setting. Stacktape detects such packages itself. Serverless applies external to every function, so each function's ZIP contains PDFKit. |
package.individually: true |
Serverless Framework | By default all functions share one ZIP. For 25 functions that ZIP holds every bundle and source map, over 600 MB unpacked; Lambda accepts at most 250 MB. |
An esbuild banner that defines require |
Serverless Framework | Serverless bundles this ES module project to an ES module, where CommonJS dependencies (ExcelJS, among others) cannot require Node.js built-ins. The banner is the fix in Serverless's build guide. Stacktape bundles to CommonJS and needs no setting. |
Load balancer health check path /health |
SST, AWS CDK | The default path / returns 404 in these applications, so the services would never become healthy. |
| A Dockerfile | SST, AWS CDK | Both need one to build a container. Stacktape, Render, Heroku and Ravion build from the source. |
--awsAccount |
Stacktape | Stacktape deploys with credentials from the Stacktape Console for a connected AWS account, not with AWS_PROFILE. |
ES module output (bundling.format: OutputFormat.ESM) with a banner that defines require |
AWS CDK | Prisma's generated client reads import.meta.url, which CDK's default CommonJS bundle leaves empty, so every function failed when Lambda loaded it. This is the same fix as Serverless's banner. |
EC2 Spot builders (build_capacity_type: ec2-spot) instead of the default sandbox builders, and Terraform pipelines on EC2 |
Ravion | Ravion's default since 12 October 2026 runs builds and Terraform in Firecracker sandboxes on a pool of EC2 hosts. In our Ravion organization every sandbox host is refused at enrollment (Ravion:SandboxHost:ENROLLMENT_REJECTED, "this organization has no machine-identity application to issue a host credential on"), so no build or Terraform run ever starts. Updating the AWS permissions to 1.0.55 did not help. Each build now starts a new c7a.4xlarge Spot instance; in our first build, about 110 seconds passed before the build itself began. A warm sandbox host starts a build in seconds, so Ravion's build times here are slower than its default would be. |
Ravion-managed domains (use_ravion_managed_domains: true) |
Ravion | The Node.js guide's configuration fails validation with the current cluster module, whose public load balancer uses HTTPS and needs a certificate. Ravion-managed domains give each service an HTTPS address under cluster.ravion.app without one. |
Left at each tool's default: the AWS SDK and Prisma. Stacktape, Serverless Framework and AWS CDK all leave
@aws-sdk/* out of the bundle, because the Node.js 22 Lambda runtime provides it, and bundle Prisma's client, whose
query compiler is plain JavaScript and WebAssembly in Prisma 7. prisma generate runs on npm ci for every tool.
- Stacktape does not have to win. The published results will show every test where another tool is faster or smaller. Source maps count toward the upload size of the tools that upload them (SST and Stacktape), and AWS CDK uploads none by default.
- The tools roll out changes differently. Serverless Framework and AWS CDK go through CloudFormation. Stacktape and CDK with hotswap update code directly. SST uses Pulumi. Render, Heroku and Ravion run their own pipelines. We measure each tool's normal path; the guarantees are not the same.
- First response. A service counts as updated at its first response from the new code. During a rolling update, some requests can still reach the old code for a short time.
- SST's packaging time includes the plan comparison of
sst diff, because SST has no packaging-only command. - CDK's and Stacktape's packaging times do not include zipping that happens only during deploy (CDK's function bundles, Stacktape's shared layers). Their upload sizes do include those ZIPs.
- Hosted builders. Render and Heroku build on the provider's machines, so their build times depend on the provider's capacity. Ravion builds on EC2 Spot instances in the connected account, which depend on Spot capacity. Render has no way to empty only the build cache, so its first-push runs need a manual cache reset.
- One machine, one region, one period. The results describe this setup, not every project.
Run on a dedicated machine in eu-west-1, the region of the deployments; we use EC2. Image sizes need Docker's
containerd image store, the default of Docker Desktop and Docker Engine 29; the older overlay2 store reports
unpacked sizes, so the harness refuses it. Everything the benchmark
creates is named bmk-*. Install the harness's own dependency (the AWS SDK, to invoke functions) and set the
credentials from the table above:
npm ci
export AWS_PROFILE=...Tools are stacktape, serverless, sst, cdk, cdk-hotswap, render, heroku and ravion. Applications are
lambda-1, lambda-5, lambda-25, container-node, container-python and five-services.
node bench.mjs setup sst lambda-5 # write the project and deploy it (not measured)
node bench.mjs package sst lambda-5 --runs 10 # package from an empty build cache
node bench.mjs package sst lambda-5 --runs 10 --warm # package after a source change
node bench.mjs package sst container-node # container image size
node bench.mjs deploy sst lambda-5 --runs 10 # deploy a code change from this machine
node bench.mjs deploy sst lambda-5 --runs 10 --change shared # change the shared service package instead
node bench.mjs remove sst lambda-5 # delete everything setup created
node report.mjs # median, fastest and slowest run of every seriessetup writes the test's branch to work/bmk-<tool>-<app>/ as its own Git repository, with the same commit as the
published branch, and installs its locked dependencies. Command logs are in logs/. SST's function packaging needs
a deployed stage, so run setup first. To spread network and AWS variation evenly, run one round of every tool
before the next round:
for round in $(seq 10); do
for tool in stacktape serverless sst cdk cdk-hotswap; do node bench.mjs deploy $tool lambda-25 --runs 1; done
doneThe published results use a Stacktape CLI built from source. Each result records the commit in stacktapeCommit.
git clone https://github.com/stacktape/stacktape && cd stacktape
git checkout d51f56ad17b9a24056046d7af99bd3716afb30da # the commit in tools/stacktape.mjs
pnpm install --frozen-lockfile
pnpm turbo run build --filter=@stacktape/init-ui
cd apps/cli
bun scripts/build-dist-package.ts --platform linux --version 4.0.0-bench.$(git rev-parse --short=8 HEAD) --keepUnarchived
export STACKTAPE_BIN=$PWD/__dist/linux/stacktapeThis is the build the release workflow makes, with Node.js 24.15, pnpm 11.17 and Bun 1.4.1. The harness checks that
the version of STACKTAPE_BIN ends in the pinned commit.
node bench.mjs push <tool> <app> --runs 5 commits a change to the project in work/, pushes it to the test's
branch on the project's origin remote and measures until the new code answers. Each run is a pair: a first push
with an empty build cache, then a second push 15 minutes after the first one finished.
You need a fork of this repository with all branches (untick "Copy the main branch only"), because the platforms
deploy from your fork's branch. The project that setup writes has the same commit as the published branch, so its
pushes continue your fork's branch. Set BENCH_FORK before setup, which adds the fork as the project's origin:
export BENCH_FORK=https://github.com/<you>/deploy-benchmarksGitHub doesn't run workflows in a new fork until you turn them on in its Actions tab. The AWS role for the workflows
must trust the fork's OIDC subject. New repositories get immutable subjects such as
repo:<you>@<owner id>/deploy-benchmarks@<repository id>:ref:refs/heads/<branch>;
gh api repos/<you>/deploy-benchmarks/actions/oidc/customization/sub shows the prefix.
Then connect the deploy service to your fork's branch. Each test branch's README has these steps for its application:
Stacktape
- Deploy the test's branch once with
stacktape deployfrom a terminal and answer yes to "Set up automatic deployments on push". This connects the repository and the test's branch to the project with the stagebmkineu-west-1. Stacktape's GitHub app must have access to the repository. - Every push then deploys on the project's runner in your AWS account: an EC2 instance with a cache disk, launched for the first push and hibernated after 15 idle minutes. The runner installs the Stacktape version set for the project in the Console from npm unless its image already contains that version. A build from source is not on npm; the main README describes how we measured one.
- To empty the runner's caches before each first push, remove the project's runner (its instance, cache disk and record) and pass
--cold-reset manual. The Console can't do this yet; see the main README.
Serverless Framework
- Turn on GitHub Actions in your fork. This branch's
.github/workflows/deploy.ymldeploys every push to the branch. - Add the repository secrets
AWS_ROLE_ARNandSERVERLESS_ACCESS_KEY.AWS_ROLE_ARNis a role in the disposable account that the fork can assume through GitHub OIDC. - Sign
ghin to an account that can manage the fork's Actions caches (gh auth loginorGH_TOKEN). The first push of each pair deletes them withgh cache delete --all.
SST
- Turn on GitHub Actions in your fork. This branch's
.github/workflows/deploy.ymldeploys every push to the branch. - Add the repository secret
AWS_ROLE_ARN.AWS_ROLE_ARNis a role in the disposable account that the fork can assume through GitHub OIDC. - Sign
ghin to an account that can manage the fork's Actions caches (gh auth loginorGH_TOKEN). The first push of each pair deletes them withgh cache delete --all.
AWS CDK
- Turn on GitHub Actions in your fork. This branch's
.github/workflows/deploy.ymldeploys every push to the branch. - Add the repository secret
AWS_ROLE_ARN.AWS_ROLE_ARNis a role in the disposable account that the fork can assume through GitHub OIDC. - Sign
ghin to an account that can manage the fork's Actions caches (gh auth loginorGH_TOKEN). The first push of each pair deletes them withgh cache delete --all.
AWS CDK with hotswap: no Git push test.
Render
- Install Render's GitHub app on your fork. With
BENCH_FORKset,node bench.mjs setupcreates the services ofrender.yamlthrough the Render API, deploying the test's branch of your fork on every push. A Blueprint created fromrender.yamlin the Render dashboard is the same. - The first push of each pair starts with a "Clear build cache & deploy" of the previous commit through the Render API and waits until it is live.
Heroku
node bench.mjs setupcreates the Heroku app. In the Heroku dashboard, connect it to your fork and the test's branch, and turn on automatic deploys.- The first push of each pair empties the build cache with
heroku builds:cache:purge.
Ravion
- In Ravion, connect your AWS account, and install Ravion's GitHub app on your fork in Settings / Git connections.
- Create the Ravion project described in the main README: a network, an ECS cluster, one web module with the repository root as its source directory, and one pipeline that builds and deploys every push to the test's branch.
- Set the
RAVION_URL_*variables to the service URLs. - The first push of each pair empties the build cache: it deletes the
buildcacheandrailpacktags from each changed service's ECR repository.
For Render, Ravion and Heroku, connect the platform before setup: they deploy only from Git, and setup checks the
deployed services.
The test branches and configs/ are generated from tools/, fixtures/,
lib.mjs and project.mjs. Don't edit them by hand. After a change to those files:
node configs.mjs # rewrite configs/, the pinned lockfiles and the generated parts of this README
git commit -am "..." # the branches are generated from committed files
node branches.mjs --push # rebuild every <app>/<tool> branch and force-push it
node branches.mjs --check # confirm that every branch on GitHub matches mainnode configs.mjs keeps the versions in each lockfile unless a pinned tool version changes. Each branch is a single
commit with a fixed author and date, so the same files always give the same commit, on any machine.
node configs.mjs --check and node branches.mjs --check change nothing and fail when something is out of date.
| Path | What it is |
|---|---|
bench.mjs |
The runner: setup, package, deploy, push and remove |
lib.mjs |
The applications, the change, running commands, the probes, the shared Dockerfile and workflow |
project.mjs |
The project of each test: the files, the commit and the branch README |
tools/ |
One module per tool: its configuration, commands, artifacts, addresses and setup notes |
configs/ |
The files each tool adds to each application, with their lockfiles, generated by node configs.mjs |
branches.mjs |
Generates the <app>/<tool> branches |
report.mjs |
Prints the median, fastest and slowest run of every series |
fixtures/ |
The test applications |
We want each tool to look the way its users would set it up. If a configuration here is worse than what you would use, or a measurement favors one tool, please open an issue in this repository. Tell us what you would change and, if you can, link the documentation that supports it. We will rerun the affected tests.