diff --git a/apps/dokploy/components/dashboard/compose/general/actions.tsx b/apps/dokploy/components/dashboard/compose/general/actions.tsx index b64d1fed9..af3e8aced 100644 --- a/apps/dokploy/components/dashboard/compose/general/actions.tsx +++ b/apps/dokploy/components/dashboard/compose/general/actions.tsx @@ -1,4 +1,11 @@ -import { Ban, CheckCircle2, RefreshCcw, Rocket, Terminal } from "lucide-react"; +import { + Ban, + CheckCircle2, + HardDriveDownload, + RefreshCcw, + Rocket, + Terminal, +} from "lucide-react"; import { useRouter } from "next/router"; import { Tooltip as TooltipPrimitive } from "radix-ui"; import { toast } from "sonner"; @@ -82,6 +89,51 @@ export const ComposeActions = ({ composeId }: Props) => { )} + {canDeploy && data?.composeType === "docker-compose" && ( + { + await deploy({ + composeId: composeId, + freshVolumes: true, + }) + .then(() => { + toast.success("Compose deployed with fresh volumes"); + refetch(); + router.push( + `/dashboard/project/${data?.environment.projectId}/environment/${data?.environmentId}/services/compose/${composeId}?tab=deployments`, + ); + }) + .catch(() => { + toast.error("Error deploying compose"); + }); + }} + > + + + + + + Fresh Volumes + + + + + + Deploy with fresh volumes (removes all persistent data) + + + + + + + )} {canDeploy && ( { diff --git a/apps/dokploy/server/queues/deployments-queue.ts b/apps/dokploy/server/queues/deployments-queue.ts index 01957e026..c6509389a 100644 --- a/apps/dokploy/server/queues/deployments-queue.ts +++ b/apps/dokploy/server/queues/deployments-queue.ts @@ -42,12 +42,14 @@ export const processDeploymentJob = async (job: InMemoryJob) => { composeId: job.data.composeId, titleLog: job.data.titleLog, descriptionLog: job.data.descriptionLog, + freshVolumes: job.data.freshVolumes, }); } else if (job.data.type === "redeploy") { await rebuildCompose({ composeId: job.data.composeId, titleLog: job.data.titleLog, descriptionLog: job.data.descriptionLog, + freshVolumes: job.data.freshVolumes, }); } } else if (job.data.applicationType === "application-preview") { diff --git a/apps/dokploy/server/queues/queue-types.ts b/apps/dokploy/server/queues/queue-types.ts index 1000725ad..5c469dd9e 100644 --- a/apps/dokploy/server/queues/queue-types.ts +++ b/apps/dokploy/server/queues/queue-types.ts @@ -16,6 +16,7 @@ type DeployJob = type: "deploy" | "redeploy"; applicationType: "compose"; serverId?: string; + freshVolumes?: boolean; } | { applicationId: string; diff --git a/packages/server/src/db/schema/compose.ts b/packages/server/src/db/schema/compose.ts index db7052dc2..ff8c25479 100644 --- a/packages/server/src/db/schema/compose.ts +++ b/packages/server/src/db/schema/compose.ts @@ -237,12 +237,14 @@ export const apiDeployCompose = z.object({ composeId: z.string().min(1), title: z.string().optional(), description: z.string().optional(), + freshVolumes: z.boolean().optional(), }); export const apiRedeployCompose = z.object({ composeId: z.string().min(1), title: z.string().optional(), description: z.string().optional(), + freshVolumes: z.boolean().optional(), }); export const apiDeleteCompose = z.object({ diff --git a/packages/server/src/services/compose.ts b/packages/server/src/services/compose.ts index 7f23c8220..d6567b0dd 100644 --- a/packages/server/src/services/compose.ts +++ b/packages/server/src/services/compose.ts @@ -229,10 +229,12 @@ export const deployCompose = async ({ composeId, titleLog = "Manual deployment", descriptionLog = "", + freshVolumes = false, }: { composeId: string; titleLog: string; descriptionLog: string; + freshVolumes?: boolean; }) => { const compose = await findComposeById(composeId); @@ -286,6 +288,16 @@ export const deployCompose = async ({ } } + if (freshVolumes && compose.composeType === "docker-compose") { + const downCommand = `set -e; env -i PATH="$PATH" docker compose -p ${compose.appName} down --volumes 2>&1 || true;`; + const downWithLog = `(${downCommand}) >> ${deployment.logPath} 2>&1`; + if (compose.serverId) { + await execAsyncRemote(compose.serverId, downWithLog); + } else { + await execAsync(downWithLog); + } + } + command = "set -e;"; command += await getBuildComposeCommand(entity); commandWithLog = `(${command}) >> ${deployment.logPath} 2>&1`; @@ -359,10 +371,12 @@ export const rebuildCompose = async ({ composeId, titleLog = "Rebuild deployment", descriptionLog = "", + freshVolumes = false, }: { composeId: string; titleLog: string; descriptionLog: string; + freshVolumes?: boolean; }) => { const compose = await findComposeById(composeId); @@ -400,6 +414,16 @@ export const rebuildCompose = async ({ } } + if (freshVolumes && compose.composeType === "docker-compose") { + const downCommand = `set -e; env -i PATH="$PATH" docker compose -p ${compose.appName} down --volumes 2>&1 || true;`; + const downWithLog = `(${downCommand}) >> ${deployment.logPath} 2>&1`; + if (compose.serverId) { + await execAsyncRemote(compose.serverId, downWithLog); + } else { + await execAsync(downWithLog); + } + } + command = "set -e;"; command += await getBuildComposeCommand(compose); commandWithLog = `(${command}) >> ${deployment.logPath} 2>&1`;
+ Deploy with fresh volumes (removes all persistent data) +