Repository navigation
self-hosted build indexer cannot fetch project env vars with runtime environment key and aborts local deployment build #3258
Description
Activity
Validated follow-up from the same live self-hosted environment:
The envvar/auth failure was real, but there was an additional important detail:
- once we routed build-time API traffic directly to the internal webapp endpoint in the host network, background worker creation succeeded during indexing
- before that, the build-stage indexer path was returning
403 Forbiddenon the background-worker creation call as well
So the observed self-hosted behavior was:
- envvar lookup path was not reliable with the deployment/build auth path
- build-time API calls from inside the build container were also sensitive to how the API URL was resolved in self-hosted mode
After bypassing the external route and using the internal webapp endpoint, we got successful worker creation during indexing and the deployment completed successfully.
This means the issue is still valid, but the build-stage auth/networking path appears to be part of the same self-hosted failure cluster.
Complete working fix: envvars + background-workers in self-hosted local build indexer
Following up with a full working solution validated on self-hosted v4.4.3 after multiple deployment cycles.
The two API calls that break the indexer RUN step
During
--local-build, the indexer script runs inside a Docker BuildKit container via the generatedContainerfileRUN step. From inside that container, it makes two API calls:Call 1:
GET /api/v1/projects/{ref}/envvarsCalled with the runtime environment key (
TRIGGER_SECRET_KEY). On self-hosted, this endpoint returns401for runtime keys (it requires a personal access token or a specific scope).The indexer treats this as fatal and aborts the build.
Working fix: Intercept the fetch call and return a fake
{variables: {}}response before it ever hits the API:if (url.includes('/envvars')) { return new Response( JSON.stringify({ variables: {} }), { status: 200, headers: { 'content-type': 'application/json' } } ); }
This is safe because env vars for the task are injected through
syncEnvVarsat build time, not at index time.
Call 2:
POST /api/v1/deployments/{id}/background-workersCalled with
TRIGGER_API_URLwhich is configured ashttp://localhost:8030. The auth key is correct (tr_prod_key works for this endpoint). Butlocalhost:8030is unreachable from inside the BuildKit container.Root cause: BuildKit's
buildx_buildkit_*container uses bridge networking even whenRUN --network=hostis specified for the build step. Inside that container,localhostrefers to the container's own loopback — not the host. The Trigger.dev webapp is not listening there.Working fix: Rewrite the URL to the Docker bridge gateway IP before the fetch:
if (url.includes('/background-workers')) { const rewritten = url.replace('localhost:8030', '172.24.1.1:8030'); // reconstruct the Request with the new URL, preserving all headers including Authorization return originalFetch(rewritten, init); }
Critical: Do NOT override the
Authorizationheader on this call. The originaltr_prod_key is valid. Replacing it withMANAGED_WORKER_SECRETor any other key causes a401.
Complete injected fetch intercept (applied to the Containerfile RUN step)
(async () => { const f = globalThis.fetch?.bind(globalThis); if (f) globalThis.fetch = async (i, n) => { const u = typeof i === 'string' ? i : i instanceof URL ? i.href : i?.url ?? ''; if (u.includes('/envvars')) return new Response(JSON.stringify({ variables: {} }), { status: 200, headers: { 'content-type': 'application/json' } }); if (u.includes('/background-workers')) { const ru = u.replace('localhost:8030', '172.24.1.1:8030'); if (typeof i === 'string') return f(ru, n); if (i instanceof URL) return f(new URL(ru), n); return f(new Request(ru, i), n); } return f(i, n); }; await import('${options.indexScript}'); })()
This is injected into the generated
ContainerfileRUNstep via a patch tobuildImage.js.
Why this is an upstream problem (not just a self-hosted ops issue)
Both failures are caused by assumptions that only hold in the Trigger.dev cloud environment:
- The API URL is assumed to be reachable from inside BuildKit containers at
localhost - The
/envvarsendpoint is assumed to accept runtime keys
A proper upstream fix would:
- Make the indexer degrade gracefully when
/envvarsreturns non-200 (treat as empty, log a warning) - Make the
TRIGGER_API_URLconfigurable for the build container network context, or default to the Docker gateway IP in self-hosted mode
Happy to provide a PR for either or both if the contributor vouch path can be resolved.
- The API URL is assumed to be reachable from inside BuildKit containers at
Thank you for the detailed write-up. We looked into this.
Authentication. The environment variables endpoint accepts the environment secret key (
tr_prod_…). This is the key that the CLI passes into the build asTRIGGER_SECRET_KEY. It is not a key-type mismatch. The webapp does not return 403 from the envvars route or the background-workers route. A 403 on both calls that disappears when you bypass your external route indicates that a component in front of the webapp (a reverse proxy or an access layer) rejects or rewrites the request. The 401 in your probe matches the response for a missing or unrecognisedAuthorizationheader.Networking. The CLI already rewrites
localhosttohost.docker.internaland adds an--add-hostentry for the build container. You can also runtrigger.dev deploy --local-build --network host. This runs the build steps and the buildx builder on the host network, so the indexer can reachlocalhost:8030directly.The patch. We cannot accept an empty environment variables response as a fallback. Indexing executes your task files with those variables, so an empty set would silently break projects that read
process.envat import time. The error must stay fatal.Possible PR. If you still cannot reach the webapp from the builder, we would welcome a PR that:
- Adds an explicit build-time API URL override (for example
--build-api-urlorTRIGGER_BUILD_API_URL) thatbuildImage.tsuses instead of the automatichost.docker.internalrewrite. - Documents the
--network hostoption in the self-hosting docs. - Includes the URL and the HTTP status in the indexer's envvars error message.
Please see the contributing guide for the vouch process before you open the PR.
- Adds an explicit build-time API URL override (for example
Picking this up. I'll implement the three items @matt-aitken listed: the build-time API URL override in
buildImage.ts, docs for--network hostand the override, and the URL plus HTTP status in the indexer's environment-variable error.@flowq-C if you're already working on it, say so and I'll step back. Otherwise I'll open a PR once my vouch request is approved.
Ready on my fork: SachinD6/trigger.dev@main...fix/local-build-api-url
It adds
--build-api-url/TRIGGER_BUILD_API_URLfor the build container, documents that plus--network host, and puts the API URL and HTTP status in the indexer's envvar error. Vouch request: #4981, and I'll open the draft PR once that is approved.Verified with the
packages/cli-v3suite (61 tests) and a stubdockerthat records the generated build args. There is no Docker on this machine, so a real self-hosted local build is still unverified.
Summary
In self-hosted Trigger.dev local deployments, the build image indexer (
managed-index-controller) fetches project environment variables during the Docker build stage. In our setup this call fails and aborts the entire deployment build.Proven behavior
managed-index-controller.mjsFailed to fetch environment variables: 403 ForbiddenGET /api/v1/projects/:projectRef/envvarstr_prod_...):401 Invalid or Missing API keyWhy this is a Trigger.dev self-hosted bug
TRIGGER_SECRET_KEYinto the image build.Repro
trigger.dev deploymanaged-index-controllerstepExpected
Actual
Failed to fetch environment variables: 403 ForbiddenEvidence
Failed to index deploymentmessage: 'Failed to fetch environment variables: 403 Forbidden'GET /api/v1/projects/proj_ncohokyumnepswndlhei/envvarstr_prod_...401 Invalid or Missing API keySuggested fix
managed-index-controllernon-fatal when envvar fetch fails