Skip to content

fix: use connection() for runtime env vars in Docker deployments#2051

Merged
nick-inkeep merged 1 commit intomainfrom
feat/docker-dynamic-env
Feb 17, 2026
Merged

fix: use connection() for runtime env vars in Docker deployments#2051
nick-inkeep merged 1 commit intomainfrom
feat/docker-dynamic-env

Conversation

@nick-inkeep
Copy link
Collaborator

@nick-inkeep nick-inkeep commented Feb 17, 2026

Summary

  • Use the stable Next.js connection() API (v15+) to opt the root layout into dynamic rendering, ensuring runtimeConfig environment variables are evaluated at request time instead of build time
  • Move runtimeConfig construction inside the component body (cleanliness improvement, not strictly required for correctness)
  • Enables a single Docker image to be promoted across environments with different env var values (build once, deploy many)

Root cause

The existing RuntimeConfigProvider pattern is architecturally correct — it reads PUBLIC_* env vars server-side in the root layout and passes them to client components via React Context. These PUBLIC_* vars (non-NEXT_PUBLIC_) are NOT inlined by turbopack — they remain as live process.env references in the compiled server-side JavaScript.

The bug is not about where env vars are read, but when the component renders.

Without any dynamic rendering signal, Next.js statically prerenders the root layout during next build. The component function runs at build time, reads process.env.PUBLIC_* (undefined/defaults in the build environment), and the resulting RSC payload — containing the serialized runtimeConfig object — is cached as static output. At runtime in Docker, the cached RSC payload is served directly; the component function never re-runs, so the containers env vars are never read.

await connection() tells Next.js to skip prerendering and re-render the layout per request. The same process.env.PUBLIC_* references now execute at request time inside the running Docker container, picking up the correct env var values.

Why connection()?

  • Official Next.js recommendation for this exact use case (Self-Hosting guide, API reference)
  • Stabilized in Next.js v15.0.0 (replaces deprecated unstable_noStore)
  • Minimal change — one import, one await
  • Works with the existing RuntimeConfigProvider pattern — no new dependencies needed

Why not alternatives?

Approach Why not
export const dynamic = force-dynamic Too coarse — applies globally via root layout config. connection() is more semantically precise
next-runtime-env library Redundant — codebase already has RuntimeConfigProvider doing the same thing
Docker entrypoint sed script Fragile community hack — breaks source maps, requires placeholder convention
unstable_noStore Deprecated in v15, replaced by connection()

Dual deployment compatibility

The PUBLIC_* || NEXT_PUBLIC_* fallback chain in runtimeConfig is preserved:

  • Vercel: NEXT_PUBLIC_* vars are inlined at build time (works as before)
  • Docker: PUBLIC_* vars (without prefix) are read at runtime via dynamic rendering

Relationship to PR #2050

Supersedes #2050. PR #2050 had the right idea (await connection()) and would have worked. This PR additionally moves runtimeConfig inside the function body as a cleanliness improvement — keeping the env var reads co-located with the dynamic rendering signal — but the critical fix in both PRs is connection() itself.

Build verification

All routes correctly show Dynamic after the change (only icon.svg remains static). Verified by inspecting compiled server-side JS that process.env.PUBLIC_* references remain as live Node.js references (not inlined). No new build, lint, or format errors introduced.

Known pre-existing issues (out of scope)

3 client components have direct process.env.NEXT_PUBLIC_* references that bypass RuntimeConfigProvider. These are inlined at build time regardless of this fix:

  • src/app/page.tsx:20NEXT_PUBLIC_TENANT_ID (falls back to default — fine for Docker)
  • src/components/sidebar-nav/app-sidebar.tsx:64NEXT_PUBLIC_ENABLE_WORK_APPS (cloud-only feature flag, off by default)
  • src/features/work-apps/slack/api/slack-api.ts:1NEXT_PUBLIC_INKEEP_AGENTS_API_URL (gated behind work apps feature flag)

These are low/no impact for Docker deployments and should be tracked independently if needed.

Test plan

  • Build succeeds (pnpm build with agents-core built first)
  • All routes show Dynamic in build output
  • Inspected compiled server-side JS — process.env.PUBLIC_* are live references, not inlined
  • Lint passes (biome lint --error-on-warnings)
  • Format passes (biome format)
  • Pre-commit hooks pass
  • Standalone runtime env var test — Built standalone output, started the same prebuilt server binary twice with different env vars:
    • With PUBLIC_INKEEP_AGENTS_API_URL=http://docker-test-api:9999, PUBLIC_SIGNOZ_URL=http://docker-test-signoz:7777, PUBLIC_NANGO_SERVER_URL=http://docker-test-nango:5555: response contains custom values, zero default values present
    • Without custom env vars: response contains defaults (localhost:3002, localhost:3050, localhost:3080), zero custom values present
    • Confirms the same prebuilt binary reads env vars at request time, not build time — exactly the Docker deployment requirement

Generated with Claude Code

…yments

Use the stable Next.js `connection()` API to opt the root layout into
dynamic rendering, ensuring runtimeConfig env vars are evaluated at
request time instead of build time. This enables a single Docker image
to be deployed across multiple environments with different env var values.

Moved the runtimeConfig construction inside the component body so it
executes per-request after `await connection()`, rather than at module
load (build time).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@changeset-bot
Copy link

changeset-bot bot commented Feb 17, 2026

🦋 Changeset detected

Latest commit: 456e16d

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 10 packages
Name Type
@inkeep/agents-manage-ui Patch
@inkeep/agents-api Patch
@inkeep/agents-cli Patch
@inkeep/agents-core Patch
@inkeep/agents-manage-mcp Patch
@inkeep/agents-mcp Patch
@inkeep/agents-sdk Patch
@inkeep/agents-work-apps Patch
@inkeep/ai-sdk-provider Patch
@inkeep/create-agents Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel
Copy link

vercel bot commented Feb 17, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agents-api Ready Ready Preview, Comment Feb 17, 2026 3:04am
agents-docs Ready Ready Preview, Comment Feb 17, 2026 3:04am
agents-manage-ui Ready Ready Preview, Comment Feb 17, 2026 3:04am

Request Review

@inkeep
Copy link
Contributor

inkeep bot commented Feb 17, 2026

📚 Docs Review: No documentation updates needed.

This PR fixes the internal implementation for runtime environment variable handling in Docker deployments. The existing deployment docs already correctly describe the PUBLIC_* environment variable pattern—this change makes the code match the documented behavior. No user-facing configuration changes are introduced.

Copy link
Contributor

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Review Summary

(0) Total Issues | Risk: Low

This is a clean, well-implemented fix. All three domain reviewers (standards, frontend, devops) returned with no findings.

Summary

The PR correctly uses the Next.js 15+ connection() API to opt the root layout into dynamic rendering, ensuring environment variables are evaluated at request time rather than build time. The key insight — moving the runtimeConfig object construction from module scope into the async component body — addresses the issue that the superseded PR #2050 missed.

What was reviewed:

  • Code quality & standards: Clean implementation, no bugs or anti-patterns detected
  • React/Next.js patterns: Proper async Server Component usage, correct connection() API placement
  • DevOps/deployment: Changeset is present and appropriate, dual-deployment pattern (PUBLIC_* || NEXT_PUBLIC_*) is preserved, Next.js version (16.1.6) fully supports the API

Strengths:

  • Minimal, surgical change — one import, function signature change, and config relocation
  • Well-documented PR description with clear rationale and alternative analysis
  • Pre-existing issues (3 client components bypassing RuntimeConfigProvider) are explicitly flagged as out-of-scope for follow-up
  • Build verification confirms all routes show ƒ (Dynamic) as expected

✅ APPROVE

Summary: This is a clean fix that follows Next.js best practices for runtime environment variable evaluation. The implementation is correct, the changeset is appropriate, and the dual Vercel/Docker deployment compatibility is preserved. Ship it! 🚀

Reviewers (3)
Reviewer Returned Main Findings Consider While You're Here Inline Comments Pending Recs Discarded
pr-review-standards 0 0 0 0 0 0 0
pr-review-frontend 0 0 0 0 0 0 0
pr-review-devops 0 0 0 0 0 0 0
Total 0 0 0 0 0 0 0

@github-actions github-actions bot deleted a comment from claude bot Feb 17, 2026
@nick-inkeep nick-inkeep merged commit 6b561f6 into main Feb 17, 2026
11 checks passed
@nick-inkeep nick-inkeep deleted the feat/docker-dynamic-env branch February 17, 2026 03:29
@nick-inkeep nick-inkeep mentioned this pull request Feb 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant