From fea3ec9a6fbdb0147ed20de96b24a803325e48ac Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Sat, 13 Dec 2025 00:57:41 -0600 Subject: [PATCH] feat(cleanup): implement background cleanup functionality - Added a new `cleanupAllBackground` function to execute Docker cleanup commands in the background, allowing for immediate return and avoiding gateway timeouts. - Refactored existing cleanup functions to utilize a centralized `cleanupCommands` object for better maintainability and readability. --- apps/dokploy/server/api/routers/settings.ts | 6 ++- packages/server/src/utils/docker/utils.ts | 46 ++++++++++++++++++--- 2 files changed, 45 insertions(+), 7 deletions(-) diff --git a/apps/dokploy/server/api/routers/settings.ts b/apps/dokploy/server/api/routers/settings.ts index 00584bf2d..a6154ec1c 100644 --- a/apps/dokploy/server/api/routers/settings.ts +++ b/apps/dokploy/server/api/routers/settings.ts @@ -3,6 +3,7 @@ import { checkGPUStatus, checkPortInUse, cleanupAll, + cleanupAllBackground, cleanupBuilders, cleanupContainers, cleanupImages, @@ -193,9 +194,10 @@ export const settingsRouter = createTRPCRouter({ cleanAll: adminProcedure .input(apiServerSchema) .mutation(async ({ input }) => { - await cleanupAll(input?.serverId); + // Execute cleanup in background and return immediately to avoid gateway timeouts + const result = await cleanupAllBackground(input?.serverId); - return true; + return result; }), cleanMonitoring: adminProcedure.mutation(async () => { if (IS_CLOUD) { diff --git a/packages/server/src/utils/docker/utils.ts b/packages/server/src/utils/docker/utils.ts index 5c7326e2d..d674a8840 100644 --- a/packages/server/src/utils/docker/utils.ts +++ b/packages/server/src/utils/docker/utils.ts @@ -171,9 +171,17 @@ ${exec} echo "Execution completed."`; +const cleanupCommands = { + containers: "docker container prune --force", + images: "docker image prune --all --force", + builders: "docker builder prune --all --force", + system: "docker system prune --all --force", + volumes: "docker volume prune --all --force", +}; + export const cleanupContainers = async (serverId?: string) => { try { - const command = "docker container prune --force"; + const command = cleanupCommands.containers; if (serverId) { await execAsyncRemote(serverId, dockerSafeExec(command)); @@ -189,7 +197,7 @@ export const cleanupContainers = async (serverId?: string) => { export const cleanupImages = async (serverId?: string) => { try { - const command = "docker image prune --all --force"; + const command = cleanupCommands.images; if (serverId) { await execAsyncRemote(serverId, dockerSafeExec(command)); @@ -203,7 +211,7 @@ export const cleanupImages = async (serverId?: string) => { export const cleanupVolumes = async (serverId?: string) => { try { - const command = "docker volume prune --all --force"; + const command = cleanupCommands.volumes; if (serverId) { await execAsyncRemote(serverId, dockerSafeExec(command)); @@ -219,7 +227,7 @@ export const cleanupVolumes = async (serverId?: string) => { export const cleanupBuilders = async (serverId?: string) => { try { - const command = "docker builder prune --all --force"; + const command = cleanupCommands.builders; if (serverId) { await execAsyncRemote(serverId, dockerSafeExec(command)); @@ -235,7 +243,7 @@ export const cleanupBuilders = async (serverId?: string) => { export const cleanupSystem = async (serverId?: string) => { try { - const command = "docker system prune --all --force"; + const command = cleanupCommands.system; if (serverId) { await execAsyncRemote(serverId, dockerSafeExec(command)); @@ -256,6 +264,34 @@ export const cleanupAll = async (serverId?: string) => { await cleanupSystem(serverId); }; +export const cleanupAllBackground = async (serverId?: string) => { + Promise.allSettled( + Object.values(cleanupCommands).map(async (command) => { + try { + if (serverId) { + await execAsyncRemote(serverId, dockerSafeExec(command)); + } else { + await execAsync(dockerSafeExec(command)); + } + } catch (error) {} + }), + ) + .then((results) => { + const failed = results.filter((r) => r.status === "rejected"); + if (failed.length > 0) { + console.error(`Docker cleanup: ${failed.length} operations failed`); + } else { + console.log("Docker cleanup completed successfully"); + } + }) + .catch((error) => console.error("Error in cleanup:", error)); + + return { + status: "scheduled", + message: "Docker cleanup has been initiated in the background", + }; +}; + export const startService = async (appName: string) => { try { await execAsync(`docker service scale ${appName}=1 `);