diff --git a/apps/dokploy/components/dashboard/docker/remove/remove-container.tsx b/apps/dokploy/components/dashboard/docker/remove/remove-container.tsx
new file mode 100644
index 000000000..3b6cd9875
--- /dev/null
+++ b/apps/dokploy/components/dashboard/docker/remove/remove-container.tsx
@@ -0,0 +1,66 @@
+import { toast } from "sonner";
+import {
+ AlertDialog,
+ AlertDialogAction,
+ AlertDialogCancel,
+ AlertDialogContent,
+ AlertDialogDescription,
+ AlertDialogFooter,
+ AlertDialogHeader,
+ AlertDialogTitle,
+ AlertDialogTrigger,
+} from "@/components/ui/alert-dialog";
+import { DropdownMenuItem } from "@/components/ui/dropdown-menu";
+import { api } from "@/utils/api";
+
+interface Props {
+ containerId: string;
+ serverId?: string;
+}
+
+export const RemoveContainerDialog = ({ containerId, serverId }: Props) => {
+ const utils = api.useUtils();
+ const { mutateAsync, isPending } = api.docker.removeContainer.useMutation();
+
+ return (
+
+
+ e.preventDefault()}
+ >
+ Remove Container
+
+
+
+
+ Are you sure?
+
+ This will permanently remove the container{" "}
+ {containerId}. If the
+ container is running, it will be forcefully stopped and removed.
+ This action cannot be undone.
+
+
+
+ Cancel
+ {
+ await mutateAsync({ containerId, serverId })
+ .then(async () => {
+ toast.success("Container removed successfully");
+ await utils.docker.getContainers.invalidate();
+ })
+ .catch((err) => {
+ toast.error(err.message);
+ });
+ }}
+ >
+ Confirm
+
+
+
+
+ );
+};
diff --git a/apps/dokploy/components/dashboard/docker/show/colums.tsx b/apps/dokploy/components/dashboard/docker/show/colums.tsx
index 74fe6819e..8507261a2 100644
--- a/apps/dokploy/components/dashboard/docker/show/colums.tsx
+++ b/apps/dokploy/components/dashboard/docker/show/colums.tsx
@@ -10,6 +10,7 @@ import {
} from "@/components/ui/dropdown-menu";
import { ShowContainerConfig } from "../config/show-container-config";
import { ShowDockerModalLogs } from "../logs/show-docker-modal-logs";
+import { RemoveContainerDialog } from "../remove/remove-container";
import { DockerTerminalModal } from "../terminal/docker-terminal-modal";
import type { Container } from "./show-containers";
@@ -127,6 +128,10 @@ export const columns: ColumnDef[] = [
>
Terminal
+
);
diff --git a/apps/dokploy/server/api/routers/docker.ts b/apps/dokploy/server/api/routers/docker.ts
index d4a7ddd57..a310d693c 100644
--- a/apps/dokploy/server/api/routers/docker.ts
+++ b/apps/dokploy/server/api/routers/docker.ts
@@ -1,4 +1,5 @@
import {
+ containerRemove,
containerRestart,
findServerById,
getConfig,
@@ -52,6 +53,32 @@ export const dockerRouter = createTRPCRouter({
return result;
}),
+ removeContainer: withPermission("docker", "read")
+ .input(
+ z.object({
+ containerId: z
+ .string()
+ .min(1)
+ .regex(containerIdRegex, "Invalid container id."),
+ serverId: z.string().optional(),
+ }),
+ )
+ .mutation(async ({ input, ctx }) => {
+ if (input.serverId) {
+ const server = await findServerById(input.serverId);
+ if (server.organizationId !== ctx.session?.activeOrganizationId) {
+ throw new TRPCError({ code: "UNAUTHORIZED" });
+ }
+ }
+ await containerRemove(input.containerId, input.serverId);
+ await audit(ctx, {
+ action: "delete",
+ resourceType: "docker",
+ resourceId: input.containerId,
+ resourceName: input.containerId,
+ });
+ }),
+
getConfig: withPermission("docker", "read")
.input(
z.object({
diff --git a/packages/server/src/services/docker.ts b/packages/server/src/services/docker.ts
index d24cae72b..92f5e3d2e 100644
--- a/packages/server/src/services/docker.ts
+++ b/packages/server/src/services/docker.ts
@@ -371,6 +371,21 @@ export const containerRestart = async (containerId: string) => {
} catch {}
};
+export const containerRemove = async (
+ containerId: string,
+ serverId?: string,
+) => {
+ const command = `docker rm -f ${containerId}`;
+ const { stderr } = serverId
+ ? await execAsyncRemote(serverId, command)
+ : await execAsync(command);
+
+ if (stderr) {
+ console.error(`Error: ${stderr}`);
+ throw new Error(stderr);
+ }
+};
+
export const getSwarmNodes = async (serverId?: string) => {
try {
let stdout = "";