To Reproduce
- Run a self-hosted / development instance from
canary (no NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY set — it isn't in apps/dokploy/.env.example).
- Log in.
- The dashboard fails to render.
Runtime IntegrationError
Missing value for Stripe(): apiKey should be a string.
Current vs. Expected behavior
Current: a self-hosted instance can't get past login. Setting NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY to any non-empty placeholder unblocks it, which is what identifies the cause.
Expected: a self-hosted instance never touches Stripe. IS_CLOUD / settings.isCloud is false, so nothing billing-related should be reachable.
What causes it
The import chain from the dashboard entry point is fully static:
apps/dokploy/pages/dashboard/home.tsx:13
→ components/dashboard/onboarding/onboarding-wizard.tsx:21
→ components/dashboard/onboarding/steps/plan-step.tsx:14
and plan-step.tsx calls Stripe at module scope, not inside the component:
const stripePromise = loadStripe(
process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY!,
);
The guard inside the wizard is a runtime one:
const { data: isCloud = true } = api.settings.isCloud.useQuery();
const visibleStepIds = isCloud ? ... : ...;
It decides which steps render. It cannot prevent the module from being imported, so loadStripe(undefined) runs on every self-hosted instance regardless. Stripe.js then throws IntegrationError because the key isn't a string.
components/dashboard/settings/billing/show-billing.tsx:45 does the same thing at module scope, and plan-step.tsx imports from it, so there are two paths to the same call.
Introduced by 2e2e0c8c2 ("feat: cloud onboarding wizard, billing trial card, and post-checkout server setup"). Server-side billing is unaffected — getBillingStatus returns early on !IS_CLOUD before creating a Stripe client. This is client-side only.
Possible fixes, in rough order of least invasive: move loadStripe inside the component (or a useMemo) so it only runs when the plan step actually renders; or lazy-load PlanStep behind the isCloud check; or make the call tolerant of a missing key.
Provide environment information
- Dokploy
v0.30.5 (canary at 1572008c)
- Self-hosted, Docker, macOS host, Node 26, Next.js 16.3.0 (Turbopack)
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY unset, as per .env.example
Which area(s) are affected? (Select all that apply)
Dashboard / UI
Are you deploying the applications where Dokploy is installed or on a remote server?
Dokploy Server
Additional context
Found while testing a DNS provider branch rebased on canary; it reproduces on plain canary and has nothing to do with that work. Worth flagging because it affects every self-hosted instance on this version, and the error message points at Stripe rather than at the onboarding wizard, which makes it easy to misattribute.
Will you send a PR to fix it?
No
To Reproduce
canary(noNEXT_PUBLIC_STRIPE_PUBLISHABLE_KEYset — it isn't inapps/dokploy/.env.example).Current vs. Expected behavior
Current: a self-hosted instance can't get past login. Setting
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEYto any non-empty placeholder unblocks it, which is what identifies the cause.Expected: a self-hosted instance never touches Stripe.
IS_CLOUD/settings.isCloudis false, so nothing billing-related should be reachable.What causes it
The import chain from the dashboard entry point is fully static:
and
plan-step.tsxcalls Stripe at module scope, not inside the component:The guard inside the wizard is a runtime one:
It decides which steps render. It cannot prevent the module from being imported, so
loadStripe(undefined)runs on every self-hosted instance regardless. Stripe.js then throwsIntegrationErrorbecause the key isn't a string.components/dashboard/settings/billing/show-billing.tsx:45does the same thing at module scope, andplan-step.tsximports from it, so there are two paths to the same call.Introduced by
2e2e0c8c2("feat: cloud onboarding wizard, billing trial card, and post-checkout server setup"). Server-side billing is unaffected —getBillingStatusreturns early on!IS_CLOUDbefore creating a Stripe client. This is client-side only.Possible fixes, in rough order of least invasive: move
loadStripeinside the component (or auseMemo) so it only runs when the plan step actually renders; or lazy-loadPlanStepbehind theisCloudcheck; or make the call tolerant of a missing key.Provide environment information
v0.30.5(canaryat1572008c)NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEYunset, as per.env.exampleWhich area(s) are affected? (Select all that apply)
Dashboard / UI
Are you deploying the applications where Dokploy is installed or on a remote server?
Dokploy Server
Additional context
Found while testing a DNS provider branch rebased on
canary; it reproduces on plaincanaryand has nothing to do with that work. Worth flagging because it affects every self-hosted instance on this version, and the error message points at Stripe rather than at the onboarding wizard, which makes it easy to misattribute.Will you send a PR to fix it?
No