Skip to content

feat(duplicate): choose the target server when duplicating services - #5451

Open
iam4x wants to merge 9 commits into
Dokploy:canaryfrom
iam4x:feat/duplicate-target-server
Open

iam4x wants to merge 9 commits into
Dokploy:canaryfrom
iam4x:feat/duplicate-target-server

Conversation

@iam4x

@iam4x iam4x commented Sep 13, 2026

Copy link
Copy Markdown

What is this PR about?

The Duplicate Services dialog gets a Target Server select. Today every duplicated application, compose stack and database keeps its source serverId, so duplication cannot be used to stage a copy on another host. This adds the missing piece asked for in #5170: a copy that lands on a different remote server, or back on the Dokploy host, while the source keeps running. #5396 (transfer) moves a service and its data; this copies configuration only, the same way duplication always has.

Duplicate dialog with the Target Server select set to remote-b

Behavior

  • The select defaults to Keep current server. When every selected service lives on the same server, the option names it and that server is not listed again. A mixed selection lists every target.
  • The list contains the Dokploy host (hidden in cloud mode and when remoteServersOnly is on) and the organization's servers that have an SSH key, the same source add-application uses.
  • Picking a different server sets serverId on every copy. Networks that are scoped to another server are dropped from networkIds (applications, databases) and from each compose serviceNetworks entry, through the same resolveNetworkIds rule the transfer feature uses. The dialog says so before you confirm.
  • Mounts, domains, ports, redirects, security, preview deployments and backups are copied exactly as before. Volumes and their data are not copied; use Transfer when you need the data to move.

API

project.duplicate accepts an optional targetServer:

targetServer?:
	| { kind: "keep" }                         // default, current behavior
	| { kind: "dokploy" }                      // place copies on the Dokploy host
	| { kind: "remote"; serverId: string }     // place copies on that server

The target is validated before any project or service row is written: dokploy is rejected in cloud mode or with remoteServersOnly; remote requires a server the session can access (owner, or a member with that serverId in accessedServers) that is active and of type deploy. Existing callers that omit targetServer are unaffected.

Changes

  • packages/server/src/services/duplicate.ts (new). assertDuplicateTargetServer, duplicateServerOverride, and duplicateService. The per-service duplication switch moved here from the router; each arm builds its payload through one helper, so the target-server override is applied in one place instead of eight. project.ts shrinks by ~390 lines.
  • packages/server/src/db/schema/duplicate.ts (new). apiDuplicateTargetServer zod union and its type.
  • packages/server/src/services/network.ts. resolveNetworkIds moved here from transfer.ts so both features share it. No behavior change.
  • apps/dokploy/server/api/routers/project.ts. duplicate takes targetServer, validates it up front, calls duplicateService, rethrows TRPCError instead of flattening every failure to BAD_REQUEST, and records the target in the audit metadata. The hand-written service type enum is now z.enum(serviceType.enumValues).
  • apps/dokploy/components/dashboard/project/duplicate-project.tsx. The select and the network warning.
  • apps/dokploy/__test__/services/duplicate-target-server.test.ts (new). 15 cases covering the three target kinds, cloud and remoteServersOnly rejection, inaccessible and inactive servers, and network filtering for networkIds and compose serviceNetworks.

Tested

Local instance on canary with two seeded remote servers, an application on each, a Postgres on the Dokploy host, a compose stack with serviceNetworks, and one server-scoped network per remote. Driven through the real dialog with Playwright and read back from Postgres:

  • Keep: every copy stays on its source server with its networks.
  • remote-b: application, compose and Postgres copies land on remote-b; the network scoped to remote-a is dropped from the application copy and from the compose serviceNetworks entry, the remote-b network is kept, detachDokployNetwork is preserved, the source rows are untouched.
  • Unknown serverId returns UNAUTHORIZED before any project row is created. An unknown kind is rejected by zod. Omitting targetServer behaves as before.
  • pnpm typecheck, pnpm server:build, biome check on the changed files clean. pnpm test: 1044 passed; the 4 failures in application.real.test.ts are pre-existing on canary on this machine (nixpacks/railpack not installed) and unrelated.

Notes for review

  • project.duplicate now rethrows TRPCError as is. Callers that keyed on every failure being BAD_REQUEST will see UNAUTHORIZED, NOT_FOUND or CONFLICT where those apply.
  • docs.dokploy.com has no page for duplication yet. I can add one to the docs repo once the API shape is settled.

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)

Related to #5170 (closed with #5396, which its description calls a different operation).

Screenshots (if applicable)

See above.

RetriggerConfidence Score: 5/5

The PR appears safe to merge; no actionable new issues remain, and both previous findings are resolved.

Summary

Adds cross-server service duplication while preserving the existing configuration-only duplication behavior.

  • Introduces a validated target-server API supporting current placement, the Dokploy host, or an accessible remote deployment server.
  • Centralizes duplication logic and filters server-scoped application, database, and Compose networks when placement changes.
  • Adds a target-server selector and network-detachment warning to the duplication dialog.
  • Reuses the transfer workflow’s network-resolution logic and adds focused authorization, placement, and network-filtering tests.

Reviews (3) · Last reviewed commit: "fix(duplicate): offer the Dokploy target..."

iam4x and others added 3 commits September 13, 2026 05:22
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@iam4x
iam4x requested a review from Siumauricio as a code owner September 13, 2026 06:01
Comment thread packages/server/package.json Outdated
Comment thread apps/dokploy/components/dashboard/project/duplicate-project.tsx Outdated
@iam4x
iam4x force-pushed the feat/duplicate-target-server branch from 00951cc to 7e86ea9 Compare September 13, 2026 06:06
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