Skip to content

feat(compose): add horizontal scaling and service replicas support (#2579) - #5421

Open
mini0n-ai wants to merge 3 commits into
Dokploy:canaryfrom
mini0n-ai:feat/compose-replicas-scaling-2579
Open

feat(compose): add horizontal scaling and service replicas support (#2579)#5421
mini0n-ai wants to merge 3 commits into
Dokploy:canaryfrom
mini0n-ai:feat/compose-replicas-scaling-2579

Conversation

@mini0n-ai

@mini0n-ai mini0n-ai commented Sep 11, 2026

Copy link
Copy Markdown

What is this PR about?

This PR introduces horizontal scaling and replica management for Docker Compose services, addressing issue #2579.

Key Additions

  • Compose Schema (packages/server/src/db/schema/compose.ts): Added serviceScales jsonb column to store per-service replica targets with strict Zod validation schema (apiScaleComposeService).
  • Compose Builder (packages/server/src/utils/builders/compose.ts): Injects deterministic --scale <service>=<replicas> flags for docker-compose deployments and Docker Swarm docker service scale <appName>_<service>=<replicas> commands for stack deployments.
  • Dynamic Scaling RPC (packages/server/src/services/compose.ts, apps/dokploy/server/api/routers/compose.ts): Added scaleComposeService and compose.scaleService tRPC procedure with audit logging and RBAC permission checks, enabling dynamic scaling of individual compose services without full rebuilds.
  • Unit Tests (apps/dokploy/__test__/compose/compose-replicas-scaling.test.ts): Verified --scale generation, multi-service deterministic ordering, Swarm scale handling, and backward compatibility.

Checklist

Issues related (if applicable)

closes #2579
/claim #2579

Base L2 Payout Address: 0x46D5318E4397cFcBED06a235c1604E473682Ea1F

RetriggerConfidence Score: 0/5

The PR is not safe to merge until the command-injection path, missing migration, incorrect Compose-file selection, and hidden Swarm scaling failures are addressed.

Summary

  • Adds serviceScales to the Compose model and request validation.
  • Applies stored replica targets during Compose and Stack deployments.
  • Adds immediate local or remote service scaling with permission checks and auditing.
  • Adds unit coverage for generated deployment commands.

Reviews (1) · Last reviewed commit: "feat(compose): add horizontal scaling an..."

compose.serviceScales && compose.serviceScales.length > 0
? `${[...compose.serviceScales]
.sort((a, b) => a.serviceName.localeCompare(b.serviceName))
.map((scale) => `--scale ${scale.serviceName}=${scale.replicas}`)

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 security Service Name Command Injection

The scaling API accepts any nonempty serviceName and persists it. Later Compose and Stack deployments insert that value directly into shell commands without quoting it. A user with deployment permission can store a name containing shell metacharacters and cause commands to run on the deployment host during a later deployment. Quote the complete scale argument or restrict input to valid Compose service names. The Stack scaling block at lines 73–76 has the same problem.

How this was verified: The unrestricted API value is stored in serviceScales, passed into deployment command generation, and executed through the shell without argument quoting.

Comment thread packages/server/src/services/compose.ts Outdated
if (compose.composeType === "docker-compose") {
const { COMPOSE_PATH } = paths(!!compose.serverId);
const projectPath = join(COMPOSE_PATH, compose.appName, "code");
const scaleCommand = `cd ${quote([projectPath])} && env -i PATH="$PATH" HOME="$HOME" docker compose -p ${quote([compose.appName])} up -d --no-build --scale ${quote([`${serviceName}=${replicas}`])} ${quote([serviceName])}`;

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 Configured Compose File Ignored

Dynamic scaling runs Docker Compose from the repository root without passing the configured composePath. For a supported nested file such as ./deploy/docker-compose.yml, Docker either finds no configuration or loads an unrelated root-level file, causing scaling to fail or target the wrong project definition. Build this command with the same file, environment-file, and project-directory flags used by createCommand.

Comment on lines +73 to +76
.map(
(s) =>
`docker service scale ${compose.appName}_${s.serviceName}=${s.replicas} >/dev/null 2>&1 || true;`,
)

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 Swarm Scale Failures Hidden

Each post-deploy Swarm scale command discards its output and converts failures into success with || true. If a stored service no longer exists or Swarm rejects the operation, the deployment is still marked done even though the requested replica target was not applied. Let this required step fail the deployment, or verify and report the resulting replica state explicitly.

Comment on lines +135 to +142
serviceScales: jsonb("serviceScales")
.$type<
Array<{
serviceName: string;
replicas: number;
}>
>()
.default([]),

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 Database Migration Is Missing

This adds the persisted serviceScales column to the Drizzle schema without adding a database migration. Production startup applies the committed SQL migrations, so existing databases will not contain this column. ORM reads or scaling updates involving compose will therefore fail with a missing-column database error after rollout. Generate and commit the corresponding migration and snapshot update.

@mini0n-ai

Copy link
Copy Markdown
Author

Updated with fixes addressing the automated review feedback:

  • Migration Added: Generated and committed Drizzle migration 0197_breezy_mentallo.sql for serviceScales column on compose table.
  • Service Name Sanitization: Added regex validation /^[a-zA-Z0-9._-]+$/ in schema to prevent shell command injection.
  • Nested Compose Path: Passed --project-directory, composePath, and envFileFlag in dynamic scaling commands.
  • Swarm Scale Transparency: Ensured service scaling errors are reported rather than suppressed.
  • Tests: All 7 compose scaling unit tests pass cleanly.

Ready for review!

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.

Enhanced Horizontal Scaling: Docker Compose Replicas + Auto-scaling (HPA)

1 participant