4 improvements and 14 server changes.
Highlights
Per-key concurrency overrides
You can now raise or lower a named concurrency limit's perKey cap for a single concurrencyKey, without touching the limit itself or any other key. Give one tenant, user, or chat session more (or fewer) concurrent runs than everyone else, straight from your code:
import { concurrencyLimits } from "@trigger.dev/sdk";// this session may run 3 at once under "subagents", for the next 7 daysawait concurrencyLimits.override("subagents", { concurrencyKey: sessionId, perKey: 3, ttl: "7d",});// read it back: the key's bound, the override, and when it expiresconst view = await concurrencyLimits.retrieve("subagents", { concurrencyKey: sessionId });console.log(view.perKey.override, view.perKey.expiresAt, view.running);// clear it earlyawait concurrencyLimits.reset("subagents", { concurrencyKey: sessionId });
Per-key overrides are temporary: they expire after ttl (7 days by default, 30 at most), and calling override again extends them. The override applies to every task holding the limit, other limits the run holds still apply on top, and perKey: 0 blocks the key entirely. Lowering a cap takes effect at the next run to start, so runs already executing continue. (c49fd05f4)
Read more in the concurrency docs.
Faster run traces for large and live runs
The run page now handles much bigger traces, and stays fast while they grow:
- Large traces load in chunks, showing up to 150k spans, instead of all at once.
- Live runs update incrementally. The page fetches only the spans written since its last read and merges them into the tree, rather than reloading the whole trace on every update.
- Child run pages read only the child's part of the trace, with the same progressive loading and live updates as root runs.
- Scrolling the trace tree stays smooth as the trace grows.
- One trace status next to Cancel and Replay tells you whether the run is live, still loading, partially shown, or failed to load (with a retry). Search, filters, and run actions share a single full-width toolbar.
The run trace API (GET /api/v1/runs/:runId/trace) can page large traces too: pass page[size] and page[after] to read every span of a run. Requests without these parameters are unchanged.
runs.replay() with options
runs.replay() now accepts an options object, so you can override the payload, metadata, tags, queue, machine, version, delay, and other trigger settings for the replay. Anything not specified carries over from the original run. Payload values are serialized the same way as trigger(), so types like Date and BigInt are preserved.
import { runs } from "@trigger.dev/sdk";await runs.replay("run_1234", { payload: { userId: "user_456" }, tags: ["replayed"], delay: "10m",});
The MCP server also gets a replay_run tool so your AI assistant can replay runs with the same options. It is hidden in --readonly mode. (ff2aa236e)
Improvements
- Self-hosted instances can require custom deploy base images per runtime via the
DEPLOY_BASE_IMAGESandDEPLOY_BUILD_BASE_IMAGESwebapp settings. The CLI builds on the images the instance specifies, and older CLIs receive an upgrade message. (#5005)
Server changes
These changes are included in the v4.7.4 Docker image and are already live on Trigger.dev Cloud:
- Recent deployments from the GitHub integration or a native CLI deploy can be redeployed from the deployments list, rebuilding the same commit or upload as a new deployment.
- The API key dialog's access summary now shows Errors and Waitpoints access. API keys with waitpoint access can create, list, and retrieve waitpoint tokens.
- Log search uses whole-word matching for ASCII alphabetic terms and substring matching for others, and incrementally searches older logs without blocking the page.
- Magic links now only sign you in from the browser that requested them. A link opened elsewhere (including by an email security scanner) no longer logs anyone in. Self-hosted instances can opt out with
MAGIC_LINK_SAME_BROWSER_REQUIRED=false. - The MFA login page now focuses the code input automatically, accepts pasted codes, and submits as soon as all six digits are entered.
- Dashboard tabs left in the background pause their live updates after 30 seconds and resume when you return.
- Preview branch auto-archiving is now always available without a feature flag.
- Blank Vercel imports no longer overwrite existing values when the default-off empty environment variable feature is enabled.
- Errors on the login page (such as expired or invalid magic links) no longer disappear moments after the page loads.
- Magic-link sign-in emails now always link to
LOGIN_ORIGIN, regardless of which host the request came in on. - Parent and root metadata updates from child tasks no longer stall behind heavily-updated parent runs, and can be buffered in Redis to survive a webapp restart.
- Revoking a personal access token is now permanent. The CLI issues a new token on next login instead of reinstating the revoked one, so you can rotate a leaked token with
trigger logoutthentrigger login.
How to upgrade
Update the trigger.dev/* packages to v4.7.4 using your package manager:
npx trigger.dev@latest update # npmpnpm dlx trigger.dev@latest update # pnpmyarn dlx trigger.dev@latest update # yarnbunx trigger.dev@latest update # bun
Self-hosted users: update your Docker image to ghcr.io/triggerdotdev/trigger.dev:v4.7.4.




