Skip to content

fix(traefik): allow disabling backend connection reuse for Swarm services - #5702

Open
WilliamAGH wants to merge 2 commits into
Dokploy:canaryfrom
WilliamAGH:fix/5281-traefik-vip-connection-reuse
Open

WilliamAGH wants to merge 2 commits into
Dokploy:canaryfrom
WilliamAGH:fix/5281-traefik-vip-connection-reuse

Conversation

@WilliamAGH

@WilliamAGH WilliamAGH commented Oct 18, 2026 •

Copy link
Copy Markdown

What is this PR about?

Dokploy routes each application domain to its Swarm service VIP (http://<appName>:<port>). Traefik pools idle backend connections by dial address, not by the task IP behind the VIP. During a rolling update, a keep-alive connection opened before the update can stay attached to a task that Swarm has already removed from service membership. New connections follow the VIP correctly; pooled ones do not.

This PR adds an application setting, Reuse backend connections (Advanced → Cluster Settings), on by default so existing behavior is unchanged. When it is turned off:

  • Dokploy renders an application-owned serversTransport named <appName>-swarm-vip with maxIdleConnsPerHost: -1 in the application's dynamic config file.
  • Every generated <appName>-service-* load balancer references that transport, so Traefik opens a new backend connection for each request and each one goes through the VIP.
  • Changing the setting rewrites the existing route files for the application and its preview deployments, so no domain needs to be re-saved. Turning it back on removes the transport and its references. A serversTransport set by hand to a different name is left alone.

Changes:

  • application.swarmVipConnectionReuse boolean column, NOT NULL DEFAULT true, with its drizzle migration. drizzle-kit also emitted the webServerSettings.whitelabelingConfig default that canary's schema already declares but no migration had generated yet; I left the generated output unedited.
  • manageDomain applies the transport when it writes a domain route.
  • updateApplication rewrites primary and preview route files only when the stored value changes.
  • Cluster Settings switch. The form now resets when a field it shows (replicas, registry, this switch) changes. Before, it reset only when the application had a custom command, so the switch and the registry select could show stale defaults. Resetting on every refetch would instead discard unsaved edits, because the application page polls every 5 seconds.

Reproduction (from #5281):

  1. Deploy an application with at least two replicas, start-first updates, and a response header that identifies the build.
  2. Send steady HTTP/1.1 keep-alive traffic through its Dokploy-generated domain.
  3. Deploy a new build.
  4. Watch the identifying header and connection errors throughout the replacement. With this setting turned off, each request opens a new connection through the VIP, so it can only reach tasks that are still in the service.

How I tested it:

  • pnpm typecheck passes.
  • vitest on __test__/traefik, __test__/drop, and __test__/server/mechanizeDockerContainer.test.ts: 77 tests pass. The new swarm-vip-transport.test.ts covers rendering the transport across several domains, removing it again while keeping a hand-set transport, and not creating a route file for an application without domains.
  • Against a real traefik:v3.6.25 container with the file provider and a traefik/whoami backend: with reuse off, two consecutive requests reached the backend from different source ports (new connection each time). After manageDomain switched reuse back on, consecutive requests shared one source port (pooled connection).
  • biome format reports no changes on the touched files.

Checklist

Before submitting this PR, please make sure that:

  • You created a dedicated branch based on the canary branch.
  • You have read the suggestions in the CONTRIBUTING.md file https://github.com/Dokploy/dokploy/blob/canary/CONTRIBUTING.md#pull-request
  • You have tested this PR in your local instance. If you have not tested it yet, please do so before submitting. This helps avoid wasting maintainers' time reviewing code that has not been verified by you.

Issues related (if applicable)

Closes #5281

Screenshots (if applicable)

RetriggerView in GreptileConfidence Score: 3/5

Fix custom transport loss and failed route update recovery before merging.

Summary

Adds an application switch to disable backend connection reuse for generated Traefik routes.

  • Application owners can turn off backend connection reuse for Swarm routes.

WilliamAGH explicitly acknowledged keeping the generated branding default change because the existing schema already declares it.

Reviews (1) · Last reviewed commit: "fix(ui): keep unsaved cluster settings a..." · Reviewed by Greptile

…ices

Traefik pools idle backend connections by dial address, so a connection
opened to http://<appName>:<port> stays bound to the task it first reached
after Swarm removes that task from the service VIP during a rolling update.

Add an application setting, on by default, that when turned off renders an
application-owned serversTransport with maxIdleConnsPerHost: -1 and points
every generated service for the application at it. Changing the setting
rewrites the existing primary and preview route files.

Fixes Dokploy#5281

Agent-Session: 9844533d-f186-4fe0-9641-6a1c2b9baf44
The application page refetches every 5 seconds, so resetting the form on every new data object discarded edits made during a deployment. Reset only when a field the form shows changes.

Agent-Session: 9844533d-f186-4fe0-9641-6a1c2b9baf44
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-18T00:26:41.936347Z f5665e5 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f5665e56d1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

delete service.loadBalancer.serversTransport;
}
} else {
service.loadBalancer.serversTransport = transportName;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve custom server transports when disabling reuse

When a generated service already references a user-defined serversTransport through the supported application Traefik editor, disabling reuse unconditionally replaces that reference with the generated transport. Options on the custom transport (for example timeouts, proxying, or TLS settings) immediately stop applying, and re-enabling reuse cannot restore the original reference because its name has been lost. Preserve non-owned references or augment their transport instead of overwriting them.

Useful? React with 👍 / 👎.

Comment on lines +67 to +71
if (
Object.keys(config.http?.routers ?? {}).length === 0 &&
Object.keys(config.http?.services ?? {}).length === 0
) {
return;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Propagate remote reconciliation read failures

For an application on a remote server, a transient SSH or cat failure is caught by loadOrCreateConfigRemote and returned as the same empty config used for a missing route file. This guard consequently returns successfully after updateApplication has already persisted the new flag, and there is no retry when the server recovers, so existing primary and preview routes keep the old connection-reuse policy while the UI shows the new value. Distinguish a missing file from a failed read and propagate or queue the latter.

Useful? React with 👍 / 👎.

Comment on lines +36 to +37
} else {
service.loadBalancer.serversTransport = transportName;

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 backend settings disappear

Turning off swarmVipConnectionReuse overwrites any custom serversTransport on generated services. Users can save these references through updateTraefikConfig, so this discards custom timeouts or TLS settings and can break routes that rely on them. Turning reuse back on removes the replacement without restoring the original reference.

Leave services with a different custom transport unchanged, as the PR description promises.

Fix in Claude Code Fix in Cursor Fix in Codex

Comment on lines +170 to +174
if (
updated &&
previous &&
previous.swarmVipConnectionReuse !== updated.swarmVipConnectionReuse
) {

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 Failed route updates cannot retry

updateApplication saves the new value before updating route files, but this comparison skips another attempt once the stored value matches. If the remote server is temporarily unreachable, loadOrCreateConfigRemote returns an empty config and the route update exits without changing anything. Write failures are also caught without being reported.

The save appears successful even though routes still use the old setting, and saving the same value again cannot repair them. Report failures and allow a retry to apply the stored setting.

Fix in Claude Code Fix in Cursor Fix in Codex

This branch has not been deployed

No deployments
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.

Traefik backend connections can outlive Swarm task membership during rolling updates

1 participant