Skip to content

[Breaking: DEPLOY SERVER BEFORE FRONT] fix: allow custom domain without Cloudflare API key#17160

Merged
FelixMalfait merged 4 commits into
twentyhq:mainfrom
VedantMadane:fix/allow-manual-custom-domain
Jan 19, 2026
Merged

[Breaking: DEPLOY SERVER BEFORE FRONT] fix: allow custom domain without Cloudflare API key#17160
FelixMalfait merged 4 commits into
twentyhq:mainfrom
VedantMadane:fix/allow-manual-custom-domain

Conversation

@VedantMadane

@VedantMadane VedantMadane commented Jan 15, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes #17101

When self-hosting TwentyCRM, users can now set workspace custom domains without requiring CLOUDFLARE_API_KEY to be configured. This enables manual DNS configuration for those not using Cloudflare.

Changes

  • Added isCloudflareConfigured method to DnsManagerService
  • Modified all Cloudflare-dependent methods to gracefully handle missing configuration
  • Added getManualDnsRecords helper that provides DNS configuration instructions when Cloudflare is not available
  • isHostnameWorking returns true in manual mode, allowing the domain to be saved and enabled
  • Added comprehensive tests for non-Cloudflare scenarios

Behavior

When CLOUDFLARE_API_KEY is set: Works exactly as before with automatic Cloudflare provisioning

When CLOUDFLARE_API_KEY is NOT set:

  • Custom domain can be saved to the database
  • User receives manual DNS configuration instructions
  • Domain is marked as working, user is responsible for external DNS and TLS configuration

Testing

Added tests covering non-Cloudflare scenarios. Full test suite requires Docker which was not run locally.


Note

Introduces a Cloudflare integration feature flag and wires it through server and front to gate the custom domain UI.

  • Server: adds isCloudflareIntegrationEnabled to ClientConfig, computed from CLOUDFLARE_API_KEY and CLOUDFLARE_ZONE_ID in client-config.service; updates GraphQL schema and unit tests.
  • Frontend: adds isCloudflareIntegrationEnabledState, extends ClientConfig type, sets the flag in useClientConfig, and conditionally renders SettingsCustomDomain in SettingsDomain when enabled.
  • Updates mocked client config to include the new flag.

Written by Cursor Bugbot for commit 0c76dde. This will update automatically on new commits. Configure here.

@github-actions

github-actions Bot commented Jan 15, 2026

Copy link
Copy Markdown
Contributor

Welcome!

Hello there, congrats on your first PR! We're excited to have you contributing to this project.
By submitting your Pull Request, you acknowledge that you agree with the terms of our Contributor License Agreement.

Generated by 🚫 dangerJS against 0c76dde

@greptile-apps

greptile-apps Bot commented Jan 15, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR enables self-hosted Twenty instances to use custom workspace domains without requiring Cloudflare API credentials. Previously, setting a custom domain would fail with an error if CLOUDFLARE_API_KEY was not configured. Now the system gracefully handles the missing Cloudflare configuration by:

  • Allowing domains to be saved to the database without Cloudflare provisioning
  • Providing manual DNS configuration instructions (CNAME records) to users
  • Automatically marking domains as "working" (trusting users to configure DNS externally)

Key changes:

  • Removed dnsManagerValidator.isCloudflareInstanceDefined() calls that threw errors
  • Added isCloudflareConfigured() method to check for Cloudflare availability
  • Added getManualDnsRecords() helper that returns DNS instructions for manual configuration
  • Modified all Cloudflare-dependent methods to gracefully return early when Cloudflare is not configured
  • Added comprehensive test coverage for non-Cloudflare scenarios

Behavior:

  • With Cloudflare API key: Works as before with automatic DNS provisioning and verification
  • Without Cloudflare API key: Domains can be saved, users receive manual instructions, no DNS verification occurs

Minor issues:

  • The isHostnameWorking() method returns true immediately in manual mode without verification, which means users are responsible for ensuring their DNS is correctly configured
  • Fallback value in getManualDnsRecords uses ?? operator which could be replaced with || for consistency

Confidence Score: 4/5

  • This PR is safe to merge with minor considerations about manual DNS validation behavior
  • The implementation is well-designed with graceful degradation when Cloudflare is not configured. Test coverage is comprehensive for the new manual mode. The main consideration is that isHostnameWorking() returns true immediately without DNS verification in manual mode, placing responsibility on users to configure DNS correctly. This is acceptable for the self-hosted use case but should be documented clearly in the UI
  • No files require special attention - the changes are localized and well-tested

Important Files Changed

Filename Overview
packages/twenty-server/src/engine/core-modules/dns-manager/services/dns-manager.service.ts Added graceful handling for missing Cloudflare configuration with manual DNS fallback; includes potential null pointer issue in getManualDnsRecords
packages/twenty-server/src/engine/core-modules/dns-manager/services/dns-manager.service.spec.ts Comprehensive test coverage added for non-Cloudflare scenarios including manual DNS mode

Sequence Diagram

sequenceDiagram
    participant User
    participant WorkspaceResolver
    participant CustomDomainManager
    participant DnsManagerService
    participant CloudflareAPI
    participant Database

    alt Cloudflare Configured
        User->>WorkspaceResolver: Set custom domain
        WorkspaceResolver->>CustomDomainManager: setCustomDomain()
        CustomDomainManager->>DnsManagerService: registerHostname()
        DnsManagerService->>DnsManagerService: isCloudflareConfigured() = true
        DnsManagerService->>CloudflareAPI: Create custom hostname
        CloudflareAPI-->>DnsManagerService: Hostname details
        DnsManagerService-->>CustomDomainManager: Success
        CustomDomainManager->>Database: Save domain to workspace
        Database-->>User: Domain saved + DNS records
        
        User->>WorkspaceResolver: Check domain status
        WorkspaceResolver->>DnsManagerService: isHostnameWorking()
        DnsManagerService->>CloudflareAPI: Check verification status
        CloudflareAPI-->>DnsManagerService: Verification status
        DnsManagerService-->>WorkspaceResolver: true/false
        WorkspaceResolver->>Database: Update isCustomDomainEnabled
    else Cloudflare NOT Configured (Manual Mode)
        User->>WorkspaceResolver: Set custom domain
        WorkspaceResolver->>CustomDomainManager: setCustomDomain()
        CustomDomainManager->>DnsManagerService: registerHostname()
        DnsManagerService->>DnsManagerService: isCloudflareConfigured() = false
        DnsManagerService-->>CustomDomainManager: undefined (skip Cloudflare)
        CustomDomainManager->>Database: Save domain to workspace
        
        User->>WorkspaceResolver: Get DNS records
        WorkspaceResolver->>DnsManagerService: getHostnameWithRecords()
        DnsManagerService->>DnsManagerService: isCloudflareConfigured() = false
        DnsManagerService->>DnsManagerService: getManualDnsRecords()
        DnsManagerService-->>WorkspaceResolver: Manual CNAME instructions
        WorkspaceResolver-->>User: Configure DNS manually
        
        User->>WorkspaceResolver: Check domain status
        WorkspaceResolver->>DnsManagerService: isHostnameWorking()
        DnsManagerService->>DnsManagerService: isCloudflareConfigured() = false
        DnsManagerService-->>WorkspaceResolver: true (always, no verification)
        WorkspaceResolver->>Database: Update isCustomDomainEnabled = true
    end
Loading

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

44 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

No issues found across 51 files

@github-actions

github-actions Bot commented Jan 15, 2026

Copy link
Copy Markdown
Contributor

🚀 Preview Environment Ready!

Your preview environment is available at: http://bore.pub:26608

This environment will automatically shut down when the PR is closed or after 5 hours.

When CLOUDFLARE_API_KEY is not set, custom domains can still be saved and
users can configure their DNS manually. This enables self-hosted instances
to use workspace custom domains without requiring Cloudflare integration.

Changes:
- DnsManagerService now gracefully handles missing Cloudflare configuration
- Added isCloudflareConfigured() method to check if Cloudflare is available
- Methods return manual DNS configuration instructions when Cloudflare is not set
- isHostnameWorking() returns true in manual mode (user responsibility)
- Added tests for non-Cloudflare scenarios

Fixes twentyhq#17101

Signed-off-by: Vedant Madane <6527493+VedantMadane@users.noreply.github.com>
@VedantMadane VedantMadane force-pushed the fix/allow-manual-custom-domain branch from 1b9ba97 to 8a54e88 Compare January 15, 2026 05:16
@FelixMalfait FelixMalfait self-assigned this Jan 15, 2026
@FelixMalfait

Copy link
Copy Markdown
Member

Thanks @VedantMadane! What you did was good but I still think we don't want to support this feature for now. This is a very niche use-case and we're trying to make Twenty much more stable and bug-free, that means reducing the surface area and not exposing this which wouldn't be properly tested (our only production use-case is with Cloudflare so if we were to break this we wouldn't see it).

@FelixMalfait FelixMalfait changed the title fix: allow custom domain without Cloudflare API key [Breaking: DEPLOY SERVER BEFORE FRONT] fix: allow custom domain without Cloudflare API key Jan 17, 2026
Comment thread packages/twenty-front/src/pages/settings/domains/SettingsDomain.tsx

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

This PR is being reviewed by Cursor Bugbot

Details

You are on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle.

To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

Comment thread packages/twenty-front/src/pages/settings/domains/SettingsDomain.tsx

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

1 issue found across 10 files (changes from recent commits).

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="packages/twenty-front/src/pages/settings/domains/SettingsDomain.tsx">

<violation number="1" location="packages/twenty-front/src/pages/settings/domains/SettingsDomain.tsx:233">
P1: Custom domain UI is hidden when Cloudflare integration is disabled, blocking manual custom domain configuration</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

<SettingsPageContainer>
<SettingsSubdomain />
<SettingsCustomDomain />
{isCloudflareIntegrationEnabled && <SettingsCustomDomain />}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1: Custom domain UI is hidden when Cloudflare integration is disabled, blocking manual custom domain configuration

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/twenty-front/src/pages/settings/domains/SettingsDomain.tsx, line 233:

<comment>Custom domain UI is hidden when Cloudflare integration is disabled, blocking manual custom domain configuration</comment>

<file context>
@@ -226,7 +230,7 @@ export const SettingsDomain = () => {
             <SettingsPageContainer>
               <SettingsSubdomain />
-              <SettingsCustomDomain />
+              {isCloudflareIntegrationEnabled && <SettingsCustomDomain />}
             </SettingsPageContainer>
           </SubMenuTopBarContainer>
</file context>

@FelixMalfait FelixMalfait force-pushed the fix/allow-manual-custom-domain branch from 24e6d57 to ab967a7 Compare January 19, 2026 10:59
@FelixMalfait FelixMalfait added this pull request to the merge queue Jan 19, 2026
@github-merge-queue github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Jan 19, 2026
@FelixMalfait FelixMalfait merged commit 4ab9523 into twentyhq:main Jan 19, 2026
69 of 71 checks passed
@twenty-eng-sync

Copy link
Copy Markdown

Hey @FelixMalfait! After you've done the QA of your Pull Request, you can mark it as done here. Thank you!

lucasbordeau pushed a commit to vasu1303/twenty that referenced this pull request Jan 21, 2026
…ut Cloudflare API key (twentyhq#17160)

## Summary

Fixes twentyhq#17101

When self-hosting TwentyCRM, users can now set workspace custom domains
without requiring CLOUDFLARE_API_KEY to be configured. This enables
manual DNS configuration for those not using Cloudflare.

## Changes

- Added isCloudflareConfigured method to DnsManagerService
- Modified all Cloudflare-dependent methods to gracefully handle missing
configuration
- Added getManualDnsRecords helper that provides DNS configuration
instructions when Cloudflare is not available
- isHostnameWorking returns true in manual mode, allowing the domain to
be saved and enabled
- Added comprehensive tests for non-Cloudflare scenarios

## Behavior

When CLOUDFLARE_API_KEY is set: Works exactly as before with automatic
Cloudflare provisioning

When CLOUDFLARE_API_KEY is NOT set:
- Custom domain can be saved to the database
- User receives manual DNS configuration instructions
- Domain is marked as working, user is responsible for external DNS and
TLS configuration

## Testing

Added tests covering non-Cloudflare scenarios. Full test suite requires
Docker which was not run locally.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Introduces a Cloudflare integration feature flag and wires it through
server and front to gate the custom domain UI.
> 
> - Server: adds `isCloudflareIntegrationEnabled` to `ClientConfig`,
computed from `CLOUDFLARE_API_KEY` and `CLOUDFLARE_ZONE_ID` in
`client-config.service`; updates GraphQL schema and unit tests.
> - Frontend: adds `isCloudflareIntegrationEnabledState`, extends
`ClientConfig` type, sets the flag in `useClientConfig`, and
conditionally renders `SettingsCustomDomain` in `SettingsDomain` when
enabled.
> - Updates mocked client config to include the new flag.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
0c76dde. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Signed-off-by: Vedant Madane <6527493+VedantMadane@users.noreply.github.com>
Co-authored-by: Félix Malfait <felix@twenty.com>
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
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.

Setting workspace custom domain fails unless CLOUDFLARE_API_KEY is set

2 participants