MidstreamDocs
When something goes wrong

Time limits

How long each stage of a deploy gets, what happens when it runs out, and what to do about a slow install or a slow start.

Every stage of a deploy has a deadline, so a hang turns into an error you can read instead of a spinner that never stops. Here is what you get.

The deadlines

StageLimitWhat happens when it runs out
Fetching your code2 minutesSANDBOX_GIT_CLONE_TIMEOUT. We treat it as our problem, not yours, and a retry usually works.
Install15 minutesSANDBOX_INSTALL_TIMEOUT. Marked transient, because a slow package registry is often a one-off.
Installing the browser your tests need3 minutesSANDBOX_BROWSER_INSTALL_TIMEOUT. Ours, not yours — it is our download, and it is skipped in a second when the container already has that browser.
The app answering on its port90 secondsSANDBOX_START_TIMEOUT. Counted from the moment your start command begins, and still the shortest budget here — see below.
The test reaching the scene3 minutesSANDBOX_TEST_TIMEOUT. The test is stopped where it stands.
The whole deploy26 minutesSANDBOX_SETUP_TIMEOUT. A backstop for a deploy that hangs somewhere without its own deadline.
A deploy that says nothing at all28 minutesMACHINE_BOOT_TIMEOUT. The last resort, for a deploy that never reported a single phase.

The order is deliberate. Each stage's budget is smaller than the whole-deploy backstop, which is smaller than the silence deadline. That way a deploy that gets stuck tells you which stage got stuck, and only a deploy that dies without a word falls through to the last line of defence. The last two are not set separately — they are worked out from the stages above them, so they always sit in that order.

Those are the numbers everyone gets. A repository whose first install is genuinely slower than that can be given more; ask us.

The stages do not all run every time. A deploy that resumes an instance skips the fetch and the install it already did, so it starts from the app.

While all this is going on, whoever clicked the link sees a loading page. It waits for as long as the deploy has — the overall limit above, and the longer one if your repository has been given more time — and only then says the deploy is taking longer than expected. They can leave at any point; the deploy carries on without them, and the instance is ready when they come back.

After the app is live

Two more limits apply once the instance is up.

If your app crashes after it started serving, we restart it twice. A third crash ends the instance with SANDBOX_APP_CRASHED.

An instance that goes fifteen minutes without a single request goes to sleep. Nothing is lost — the next visit wakes it, and the work it already did is still done, so waking is quick. This is why the second person to open an instance has a very different experience from the first.

If your install is too slow

Fifteen minutes covers installing dependencies and building an application, from nothing — no package cache, no browser download, no container images, because a deploy starts with your repository and nothing else. It is sized for a first deploy. Later ones reuse what that one did, which is why they are so much faster.

If you are running out of it:

  • Install from a lockfile. npm ci, pnpm install --frozen-lockfile, or your package manager's equivalent. Resolving a dependency graph from scratch is most of the cost.
  • Skip what a deploy does not need. Anything that only matters for local development or for publishing is wasted time here.
  • Do not download browsers you will not drive. The deploy runs your test in one browser. Installing three is three times the download.
  • Split heavy work sensibly. If your build genuinely takes minutes, the install stage is where it belongs — it has the larger budget, and it is skipped entirely when an instance resumes.

If your app is slow to start

Ninety seconds is the tightest budget in the deploy, and it is the one people hit. It starts when your start command starts, and it ends when something answers a request at the root of your app — so it covers everything that command does on the way there, not just the server at the end of it.

  • Serve a production build. A development server that compiles the page on first request is the single most common cause of this failure. Build during install, serve during start.
  • Listen on PORT. We give your app a port in the PORT environment variable and poll that port only. An app hardcoded to 3000 while we watch a different port never looks ready.
  • Move setup out of start. Migrations, seed data, asset generation, waiting on a container to come up — all of it counts against the ninety seconds. Put it in the install command instead.
  • Answer the root URL. A redirect is fine and so is a 404 — we only need something to respond. An app that hangs the connection while it warms up does not count as answering.

If your test is slow to reach the scene

Three minutes is the whole run, from the first line of the test to the scene call. Nothing after the scene matters, because we stop the test there.

  • Mark scenes early in a flow. A scene at the end of a ten-step wizard has to replay all ten steps on every deploy.
  • Do not wait on the real world. A test that polls an external API, waits on an email, or sleeps to be safe spends your budget on nothing.
  • Reproduce locally. npx midstream your-scene-slug runs the same test headed and stops at the scene, which tells you exactly where the time goes.

If a deploy hit a deadline, the phase timeline shows which one and how long it ran — see Reading a deploy's timeline.