From 43964510956839ec7253eaae5b04c33f7a96ee1d Mon Sep 17 00:00:00 2001 From: Souvik Kumar Date: Tue, 29 Sep 2026 18:37:57 +0530 Subject: [PATCH 1/4] fix: accept deploy webhook payloads up to 25 MB --- apps/dokploy/pages/api/deploy/[refreshToken].ts | 8 ++++++++ apps/dokploy/pages/api/deploy/compose/[refreshToken].ts | 8 ++++++++ apps/dokploy/pages/api/deploy/github.ts | 8 ++++++++ 3 files changed, 24 insertions(+) diff --git a/apps/dokploy/pages/api/deploy/[refreshToken].ts b/apps/dokploy/pages/api/deploy/[refreshToken].ts index 9cf6142d2..988a344d4 100644 --- a/apps/dokploy/pages/api/deploy/[refreshToken].ts +++ b/apps/dokploy/pages/api/deploy/[refreshToken].ts @@ -32,6 +32,14 @@ const getPackageVersion = (headers: any, body: any) => { return null; }; +export const config = { + api: { + bodyParser: { + sizeLimit: "25mb", + }, + }, +}; + export default async function handler( req: NextApiRequest, res: NextApiResponse, diff --git a/apps/dokploy/pages/api/deploy/compose/[refreshToken].ts b/apps/dokploy/pages/api/deploy/compose/[refreshToken].ts index 0d83c1017..06b4e768d 100644 --- a/apps/dokploy/pages/api/deploy/compose/[refreshToken].ts +++ b/apps/dokploy/pages/api/deploy/compose/[refreshToken].ts @@ -15,6 +15,14 @@ import { logWebhookError, } from "../[refreshToken]"; +export const config = { + api: { + bodyParser: { + sizeLimit: "25mb", + }, + }, +}; + export default async function handler( req: NextApiRequest, res: NextApiResponse, diff --git a/apps/dokploy/pages/api/deploy/github.ts b/apps/dokploy/pages/api/deploy/github.ts index 2273ac3bc..bb39ed7d5 100644 --- a/apps/dokploy/pages/api/deploy/github.ts +++ b/apps/dokploy/pages/api/deploy/github.ts @@ -26,6 +26,14 @@ import { const getGithubRepositoryOwner = (githubBody: any) => githubBody?.repository?.owner?.name ?? githubBody?.repository?.owner?.login; +export const config = { + api: { + bodyParser: { + sizeLimit: "25mb", + }, + }, +}; + export default async function handler( req: NextApiRequest, res: NextApiResponse, From e8be2693e0edd8c9b753b42910c012ea2a0fd599 Mon Sep 17 00:00:00 2001 From: Souvik Kumar Date: Tue, 29 Sep 2026 19:06:44 +0530 Subject: [PATCH 2/4] refactor: make deploy webhook body limit configurable via DEPLOY_WEBHOOK_MAX_BODY_SIZE --- .../__test__/deploy/github-webhook-handler.test.ts | 1 + apps/dokploy/pages/api/deploy/[refreshToken].ts | 3 ++- apps/dokploy/pages/api/deploy/compose/[refreshToken].ts | 8 ++++++-- apps/dokploy/pages/api/deploy/github.ts | 3 ++- packages/server/src/constants/index.ts | 7 ++++++- 5 files changed, 17 insertions(+), 5 deletions(-) diff --git a/apps/dokploy/__test__/deploy/github-webhook-handler.test.ts b/apps/dokploy/__test__/deploy/github-webhook-handler.test.ts index 6b61c97d7..43594848a 100644 --- a/apps/dokploy/__test__/deploy/github-webhook-handler.test.ts +++ b/apps/dokploy/__test__/deploy/github-webhook-handler.test.ts @@ -63,6 +63,7 @@ vi.mock("@dokploy/server/db", () => ({ })); vi.mock("@dokploy/server", () => ({ + DEPLOY_WEBHOOK_MAX_BODY_SIZE: 25 * 1024 * 1024, IS_CLOUD: false, shouldDeploy: mocks.shouldDeploy, checkUserRepositoryPermissions: vi.fn(), diff --git a/apps/dokploy/pages/api/deploy/[refreshToken].ts b/apps/dokploy/pages/api/deploy/[refreshToken].ts index 988a344d4..5fa176bdf 100644 --- a/apps/dokploy/pages/api/deploy/[refreshToken].ts +++ b/apps/dokploy/pages/api/deploy/[refreshToken].ts @@ -1,5 +1,6 @@ import { type Bitbucket, + DEPLOY_WEBHOOK_MAX_BODY_SIZE, getBitbucketHeaders, IS_CLOUD, shouldDeploy, @@ -35,7 +36,7 @@ const getPackageVersion = (headers: any, body: any) => { export const config = { api: { bodyParser: { - sizeLimit: "25mb", + sizeLimit: DEPLOY_WEBHOOK_MAX_BODY_SIZE, }, }, }; diff --git a/apps/dokploy/pages/api/deploy/compose/[refreshToken].ts b/apps/dokploy/pages/api/deploy/compose/[refreshToken].ts index 06b4e768d..f11f96c10 100644 --- a/apps/dokploy/pages/api/deploy/compose/[refreshToken].ts +++ b/apps/dokploy/pages/api/deploy/compose/[refreshToken].ts @@ -1,4 +1,8 @@ -import { IS_CLOUD, shouldDeploy } from "@dokploy/server"; +import { + DEPLOY_WEBHOOK_MAX_BODY_SIZE, + IS_CLOUD, + shouldDeploy, +} from "@dokploy/server"; import { db } from "@dokploy/server/db"; import { eq } from "drizzle-orm"; import type { NextApiRequest, NextApiResponse } from "next"; @@ -18,7 +22,7 @@ import { export const config = { api: { bodyParser: { - sizeLimit: "25mb", + sizeLimit: DEPLOY_WEBHOOK_MAX_BODY_SIZE, }, }, }; diff --git a/apps/dokploy/pages/api/deploy/github.ts b/apps/dokploy/pages/api/deploy/github.ts index bb39ed7d5..713e93d79 100644 --- a/apps/dokploy/pages/api/deploy/github.ts +++ b/apps/dokploy/pages/api/deploy/github.ts @@ -2,6 +2,7 @@ import { checkUserRepositoryPermissions, createPreviewDeployment, createSecurityBlockedComment, + DEPLOY_WEBHOOK_MAX_BODY_SIZE, findGithubById, findPreviewDeploymentByApplicationId, findPreviewDeploymentsByPullRequestId, @@ -29,7 +30,7 @@ const getGithubRepositoryOwner = (githubBody: any) => export const config = { api: { bodyParser: { - sizeLimit: "25mb", + sizeLimit: DEPLOY_WEBHOOK_MAX_BODY_SIZE, }, }, }; diff --git a/packages/server/src/constants/index.ts b/packages/server/src/constants/index.ts index c303041f3..133a09f7a 100644 --- a/packages/server/src/constants/index.ts +++ b/packages/server/src/constants/index.ts @@ -13,7 +13,7 @@ export const DOKPLOY_DOCKER_PORT = process.env.DOKPLOY_DOCKER_PORT export const CLEANUP_CRON_JOB = "50 23 * * *"; -// Body size limits for the OpenAPI catch-all route (pages/api/[...trpc].ts). +// Body size limits for the OpenAPI catch-all route (pages/api/[...trpc].ts) and the deploy webhooks (pages/api/deploy/*). const parseByteSize = (envVar: string, fallback: number): number => { const raw = process.env[envVar]; if (!raw) return fallback; @@ -35,6 +35,11 @@ export const OPENAPI_MAX_UPLOAD_SIZE = parseByteSize( 1024 * 1024 * 1024, // 1gb ); +export const DEPLOY_WEBHOOK_MAX_BODY_SIZE = parseByteSize( + "DEPLOY_WEBHOOK_MAX_BODY_SIZE", + 25 * 1024 * 1024, // 25mb, GitHub's max webhook payload +); + type DockerSocketCandidate = { label: string; path: string; From 1c6d333739df7502bf8c3752aa236e253a6422a4 Mon Sep 17 00:00:00 2001 From: Souvik Kumar Date: Tue, 29 Sep 2026 19:20:41 +0530 Subject: [PATCH 3/4] fix: parse deploy webhook bodies in the handler so the size limit can be configured --- .../__test__/deploy/github-webhook-handler.test.ts | 4 ++++ apps/dokploy/pages/api/deploy/[refreshToken].ts | 10 +++++++--- .../dokploy/pages/api/deploy/compose/[refreshToken].ts | 10 +++++++--- apps/dokploy/pages/api/deploy/github.ts | 10 +++++++--- 4 files changed, 25 insertions(+), 9 deletions(-) diff --git a/apps/dokploy/__test__/deploy/github-webhook-handler.test.ts b/apps/dokploy/__test__/deploy/github-webhook-handler.test.ts index 43594848a..d11c9c736 100644 --- a/apps/dokploy/__test__/deploy/github-webhook-handler.test.ts +++ b/apps/dokploy/__test__/deploy/github-webhook-handler.test.ts @@ -62,6 +62,10 @@ vi.mock("@dokploy/server/db", () => ({ }, })); +vi.mock("next/dist/server/api-utils/node/parse-body", () => ({ + parseBody: async (req: { body: unknown }) => req.body, +})); + vi.mock("@dokploy/server", () => ({ DEPLOY_WEBHOOK_MAX_BODY_SIZE: 25 * 1024 * 1024, IS_CLOUD: false, diff --git a/apps/dokploy/pages/api/deploy/[refreshToken].ts b/apps/dokploy/pages/api/deploy/[refreshToken].ts index 5fa176bdf..03430ea08 100644 --- a/apps/dokploy/pages/api/deploy/[refreshToken].ts +++ b/apps/dokploy/pages/api/deploy/[refreshToken].ts @@ -8,6 +8,7 @@ import { import { db } from "@dokploy/server/db"; import { eq } from "drizzle-orm"; import type { NextApiRequest, NextApiResponse } from "next"; +import { parseBody } from "next/dist/server/api-utils/node/parse-body"; import { applications } from "@/server/db/schema"; import type { DeploymentJob } from "@/server/queues/queue-types"; import { myQueue } from "@/server/queues/queueSetup"; @@ -35,9 +36,7 @@ const getPackageVersion = (headers: any, body: any) => { export const config = { api: { - bodyParser: { - sizeLimit: DEPLOY_WEBHOOK_MAX_BODY_SIZE, - }, + bodyParser: false, }, }; @@ -45,6 +44,11 @@ export default async function handler( req: NextApiRequest, res: NextApiResponse, ) { + if (Number(req.headers["content-length"]) > DEPLOY_WEBHOOK_MAX_BODY_SIZE) { + res.status(413).json({ message: "Payload too large" }); + return; + } + req.body = await parseBody(req, DEPLOY_WEBHOOK_MAX_BODY_SIZE); const { refreshToken } = req.query; try { if (req.headers["x-github-event"] === "ping") { diff --git a/apps/dokploy/pages/api/deploy/compose/[refreshToken].ts b/apps/dokploy/pages/api/deploy/compose/[refreshToken].ts index f11f96c10..fc7d3c54e 100644 --- a/apps/dokploy/pages/api/deploy/compose/[refreshToken].ts +++ b/apps/dokploy/pages/api/deploy/compose/[refreshToken].ts @@ -6,6 +6,7 @@ import { import { db } from "@dokploy/server/db"; import { eq } from "drizzle-orm"; import type { NextApiRequest, NextApiResponse } from "next"; +import { parseBody } from "next/dist/server/api-utils/node/parse-body"; import { compose } from "@/server/db/schema"; import type { DeploymentJob } from "@/server/queues/queue-types"; import { myQueue } from "@/server/queues/queueSetup"; @@ -21,9 +22,7 @@ import { export const config = { api: { - bodyParser: { - sizeLimit: DEPLOY_WEBHOOK_MAX_BODY_SIZE, - }, + bodyParser: false, }, }; @@ -31,6 +30,11 @@ export default async function handler( req: NextApiRequest, res: NextApiResponse, ) { + if (Number(req.headers["content-length"]) > DEPLOY_WEBHOOK_MAX_BODY_SIZE) { + res.status(413).json({ message: "Payload too large" }); + return; + } + req.body = await parseBody(req, DEPLOY_WEBHOOK_MAX_BODY_SIZE); const { refreshToken } = req.query; try { if (req.headers["x-github-event"] === "ping") { diff --git a/apps/dokploy/pages/api/deploy/github.ts b/apps/dokploy/pages/api/deploy/github.ts index 713e93d79..55427c10f 100644 --- a/apps/dokploy/pages/api/deploy/github.ts +++ b/apps/dokploy/pages/api/deploy/github.ts @@ -14,6 +14,7 @@ import { db } from "@dokploy/server/db"; import { Webhooks } from "@octokit/webhooks"; import { and, eq } from "drizzle-orm"; import type { NextApiRequest, NextApiResponse } from "next"; +import { parseBody } from "next/dist/server/api-utils/node/parse-body"; import { applications, compose, github } from "@/server/db/schema"; import type { DeploymentJob } from "@/server/queues/queue-types"; import { myQueue } from "@/server/queues/queueSetup"; @@ -29,9 +30,7 @@ const getGithubRepositoryOwner = (githubBody: any) => export const config = { api: { - bodyParser: { - sizeLimit: DEPLOY_WEBHOOK_MAX_BODY_SIZE, - }, + bodyParser: false, }, }; @@ -39,6 +38,11 @@ export default async function handler( req: NextApiRequest, res: NextApiResponse, ) { + if (Number(req.headers["content-length"]) > DEPLOY_WEBHOOK_MAX_BODY_SIZE) { + res.status(413).json({ message: "Payload too large" }); + return; + } + req.body = await parseBody(req, DEPLOY_WEBHOOK_MAX_BODY_SIZE); const signature = req.headers["x-hub-signature-256"]; if (!signature) { res.status(401).json({ message: "Missing signature header" }); From 82545bf500a729bf4e5deb697a215931a6adb5d9 Mon Sep 17 00:00:00 2001 From: Souvik Kumar Date: Tue, 29 Sep 2026 20:00:05 +0530 Subject: [PATCH 4/4] revert: go back to a literal 25mb body limit for deploy webhooks --- .../deploy/github-webhook-handler.test.ts | 5 ----- apps/dokploy/pages/api/deploy/[refreshToken].ts | 11 +++-------- .../pages/api/deploy/compose/[refreshToken].ts | 16 ++++------------ apps/dokploy/pages/api/deploy/github.ts | 11 +++-------- packages/server/src/constants/index.ts | 7 +------ 5 files changed, 11 insertions(+), 39 deletions(-) diff --git a/apps/dokploy/__test__/deploy/github-webhook-handler.test.ts b/apps/dokploy/__test__/deploy/github-webhook-handler.test.ts index d11c9c736..6b61c97d7 100644 --- a/apps/dokploy/__test__/deploy/github-webhook-handler.test.ts +++ b/apps/dokploy/__test__/deploy/github-webhook-handler.test.ts @@ -62,12 +62,7 @@ vi.mock("@dokploy/server/db", () => ({ }, })); -vi.mock("next/dist/server/api-utils/node/parse-body", () => ({ - parseBody: async (req: { body: unknown }) => req.body, -})); - vi.mock("@dokploy/server", () => ({ - DEPLOY_WEBHOOK_MAX_BODY_SIZE: 25 * 1024 * 1024, IS_CLOUD: false, shouldDeploy: mocks.shouldDeploy, checkUserRepositoryPermissions: vi.fn(), diff --git a/apps/dokploy/pages/api/deploy/[refreshToken].ts b/apps/dokploy/pages/api/deploy/[refreshToken].ts index 03430ea08..988a344d4 100644 --- a/apps/dokploy/pages/api/deploy/[refreshToken].ts +++ b/apps/dokploy/pages/api/deploy/[refreshToken].ts @@ -1,6 +1,5 @@ import { type Bitbucket, - DEPLOY_WEBHOOK_MAX_BODY_SIZE, getBitbucketHeaders, IS_CLOUD, shouldDeploy, @@ -8,7 +7,6 @@ import { import { db } from "@dokploy/server/db"; import { eq } from "drizzle-orm"; import type { NextApiRequest, NextApiResponse } from "next"; -import { parseBody } from "next/dist/server/api-utils/node/parse-body"; import { applications } from "@/server/db/schema"; import type { DeploymentJob } from "@/server/queues/queue-types"; import { myQueue } from "@/server/queues/queueSetup"; @@ -36,7 +34,9 @@ const getPackageVersion = (headers: any, body: any) => { export const config = { api: { - bodyParser: false, + bodyParser: { + sizeLimit: "25mb", + }, }, }; @@ -44,11 +44,6 @@ export default async function handler( req: NextApiRequest, res: NextApiResponse, ) { - if (Number(req.headers["content-length"]) > DEPLOY_WEBHOOK_MAX_BODY_SIZE) { - res.status(413).json({ message: "Payload too large" }); - return; - } - req.body = await parseBody(req, DEPLOY_WEBHOOK_MAX_BODY_SIZE); const { refreshToken } = req.query; try { if (req.headers["x-github-event"] === "ping") { diff --git a/apps/dokploy/pages/api/deploy/compose/[refreshToken].ts b/apps/dokploy/pages/api/deploy/compose/[refreshToken].ts index fc7d3c54e..06b4e768d 100644 --- a/apps/dokploy/pages/api/deploy/compose/[refreshToken].ts +++ b/apps/dokploy/pages/api/deploy/compose/[refreshToken].ts @@ -1,12 +1,7 @@ -import { - DEPLOY_WEBHOOK_MAX_BODY_SIZE, - IS_CLOUD, - shouldDeploy, -} from "@dokploy/server"; +import { IS_CLOUD, shouldDeploy } from "@dokploy/server"; import { db } from "@dokploy/server/db"; import { eq } from "drizzle-orm"; import type { NextApiRequest, NextApiResponse } from "next"; -import { parseBody } from "next/dist/server/api-utils/node/parse-body"; import { compose } from "@/server/db/schema"; import type { DeploymentJob } from "@/server/queues/queue-types"; import { myQueue } from "@/server/queues/queueSetup"; @@ -22,7 +17,9 @@ import { export const config = { api: { - bodyParser: false, + bodyParser: { + sizeLimit: "25mb", + }, }, }; @@ -30,11 +27,6 @@ export default async function handler( req: NextApiRequest, res: NextApiResponse, ) { - if (Number(req.headers["content-length"]) > DEPLOY_WEBHOOK_MAX_BODY_SIZE) { - res.status(413).json({ message: "Payload too large" }); - return; - } - req.body = await parseBody(req, DEPLOY_WEBHOOK_MAX_BODY_SIZE); const { refreshToken } = req.query; try { if (req.headers["x-github-event"] === "ping") { diff --git a/apps/dokploy/pages/api/deploy/github.ts b/apps/dokploy/pages/api/deploy/github.ts index 55427c10f..bb39ed7d5 100644 --- a/apps/dokploy/pages/api/deploy/github.ts +++ b/apps/dokploy/pages/api/deploy/github.ts @@ -2,7 +2,6 @@ import { checkUserRepositoryPermissions, createPreviewDeployment, createSecurityBlockedComment, - DEPLOY_WEBHOOK_MAX_BODY_SIZE, findGithubById, findPreviewDeploymentByApplicationId, findPreviewDeploymentsByPullRequestId, @@ -14,7 +13,6 @@ import { db } from "@dokploy/server/db"; import { Webhooks } from "@octokit/webhooks"; import { and, eq } from "drizzle-orm"; import type { NextApiRequest, NextApiResponse } from "next"; -import { parseBody } from "next/dist/server/api-utils/node/parse-body"; import { applications, compose, github } from "@/server/db/schema"; import type { DeploymentJob } from "@/server/queues/queue-types"; import { myQueue } from "@/server/queues/queueSetup"; @@ -30,7 +28,9 @@ const getGithubRepositoryOwner = (githubBody: any) => export const config = { api: { - bodyParser: false, + bodyParser: { + sizeLimit: "25mb", + }, }, }; @@ -38,11 +38,6 @@ export default async function handler( req: NextApiRequest, res: NextApiResponse, ) { - if (Number(req.headers["content-length"]) > DEPLOY_WEBHOOK_MAX_BODY_SIZE) { - res.status(413).json({ message: "Payload too large" }); - return; - } - req.body = await parseBody(req, DEPLOY_WEBHOOK_MAX_BODY_SIZE); const signature = req.headers["x-hub-signature-256"]; if (!signature) { res.status(401).json({ message: "Missing signature header" }); diff --git a/packages/server/src/constants/index.ts b/packages/server/src/constants/index.ts index 133a09f7a..c303041f3 100644 --- a/packages/server/src/constants/index.ts +++ b/packages/server/src/constants/index.ts @@ -13,7 +13,7 @@ export const DOKPLOY_DOCKER_PORT = process.env.DOKPLOY_DOCKER_PORT export const CLEANUP_CRON_JOB = "50 23 * * *"; -// Body size limits for the OpenAPI catch-all route (pages/api/[...trpc].ts) and the deploy webhooks (pages/api/deploy/*). +// Body size limits for the OpenAPI catch-all route (pages/api/[...trpc].ts). const parseByteSize = (envVar: string, fallback: number): number => { const raw = process.env[envVar]; if (!raw) return fallback; @@ -35,11 +35,6 @@ export const OPENAPI_MAX_UPLOAD_SIZE = parseByteSize( 1024 * 1024 * 1024, // 1gb ); -export const DEPLOY_WEBHOOK_MAX_BODY_SIZE = parseByteSize( - "DEPLOY_WEBHOOK_MAX_BODY_SIZE", - 25 * 1024 * 1024, // 25mb, GitHub's max webhook payload -); - type DockerSocketCandidate = { label: string; path: string;