From 439fba1f4bcba4b696a1ee67e1d488aa3dcab8aa Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Sat, 6 Dec 2025 13:58:03 -0600 Subject: [PATCH] fix: improve error handling for Traefik port updates and enhance port availability checks --- .../web-server/manage-traefik-ports.tsx | 4 +- apps/dokploy/server/api/routers/settings.ts | 13 ++++ packages/server/src/services/docker.ts | 6 +- packages/server/src/services/settings.ts | 68 +++---------------- 4 files changed, 28 insertions(+), 63 deletions(-) diff --git a/apps/dokploy/components/dashboard/settings/web-server/manage-traefik-ports.tsx b/apps/dokploy/components/dashboard/settings/web-server/manage-traefik-ports.tsx index 282f1fddd..c67422220 100644 --- a/apps/dokploy/components/dashboard/settings/web-server/manage-traefik-ports.tsx +++ b/apps/dokploy/components/dashboard/settings/web-server/manage-traefik-ports.tsx @@ -105,7 +105,9 @@ export const ManageTraefikPorts = ({ children, serverId }: Props) => { }); toast.success(t("settings.server.webServer.traefik.portsUpdated")); setOpen(false); - } catch {} + } catch (error) { + toast.error((error as Error).message || "Error updating Traefik ports"); + } }; return ( diff --git a/apps/dokploy/server/api/routers/settings.ts b/apps/dokploy/server/api/routers/settings.ts index 90f895d0d..4894859df 100644 --- a/apps/dokploy/server/api/routers/settings.ts +++ b/apps/dokploy/server/api/routers/settings.ts @@ -822,6 +822,19 @@ export const settingsRouter = createTRPCRouter({ "dokploy-traefik", input?.serverId, ); + + for (const port of input.additionalPorts) { + const portCheck = await checkPortInUse( + port.publishedPort, + input.serverId, + ); + if (portCheck.isInUse) { + throw new TRPCError({ + code: "CONFLICT", + message: `Port ${port.targetPort} is already in use by ${portCheck.conflictingContainer}`, + }); + } + } const preparedEnv = prepareEnvironmentVariables(env); await writeTraefikSetup({ diff --git a/packages/server/src/services/docker.ts b/packages/server/src/services/docker.ts index ab454b7ea..2194c89c6 100644 --- a/packages/server/src/services/docker.ts +++ b/packages/server/src/services/docker.ts @@ -290,10 +290,10 @@ export const getContainersByAppLabel = async ( const command = type === "swarm" - ? `docker ps -a --filter "label=com.docker.swarm.service.name=${appName}" --format 'CONTAINER ID : {{.ID}} | Name: {{.Names}} | State: {{.State}}'` + ? `docker ps --filter "label=com.docker.swarm.service.name=${appName}" --format 'CONTAINER ID : {{.ID}} | Name: {{.Names}} | State: {{.State}}'` : type === "standalone" - ? `docker ps -a --filter "name=${appName}" --format 'CONTAINER ID : {{.ID}} | Name: {{.Names}} | State: {{.State}}'` - : `docker ps -a --filter "label=com.docker.compose.project=${appName}" --format 'CONTAINER ID : {{.ID}} | Name: {{.Names}} | State: {{.State}}'`; + ? `docker ps --filter "name=${appName}" --format 'CONTAINER ID : {{.ID}} | Name: {{.Names}} | State: {{.State}}'` + : `docker ps --filter "label=com.docker.compose.project=${appName}" --format 'CONTAINER ID : {{.ID}} | Name: {{.Names}} | State: {{.State}}'`; if (serverId) { const result = await execAsyncRemote(serverId, command); stdout = result.stdout; diff --git a/packages/server/src/services/settings.ts b/packages/server/src/services/settings.ts index a33ea9988..3a7222dab 100644 --- a/packages/server/src/services/settings.ts +++ b/packages/server/src/services/settings.ts @@ -392,73 +392,23 @@ export const readPorts = async ( ); }; -/** - * Check if a port is already in use by another container - * @param port - The port number to check - * @param serverId - Optional server ID for remote Docker - * @returns Object with isInUse boolean and conflictingContainer name if found - */ export const checkPortInUse = async ( port: number, serverId?: string, ): Promise<{ isInUse: boolean; conflictingContainer?: string }> => { try { - // Method 1: Check all containers and inspect their port bindings - const listContainersCommand = `docker ps -a --format '{{.Names}}'`; - let containersList = ""; - if (serverId) { - const result = await execAsyncRemote(serverId, listContainersCommand); - containersList = result.stdout.trim(); - } else { - const result = await execAsync(listContainersCommand); - containersList = result.stdout.trim(); - } + const command = `docker ps -a --format '{{.Names}}' | grep -v '^dokploy-traefik$' | while read name; do docker port "$name" 2>/dev/null | grep -q ':${port}' && echo "$name" && break; done || true`; + const { stdout } = serverId + ? await execAsyncRemote(serverId, command) + : await execAsync(command); - const containerNames = containersList - .split("\n") - .filter((name) => name && name !== "dokploy-traefik"); + const container = stdout.trim(); - // Check each container's port bindings - for (const containerName of containerNames) { - const portCheckCommand = `docker port ${containerName} 2>/dev/null | grep ':${port}' || true`; - let portOutput = ""; - if (serverId) { - const result = await execAsyncRemote(serverId, portCheckCommand); - portOutput = result.stdout.trim(); - } else { - const result = await execAsync(portCheckCommand); - portOutput = result.stdout.trim(); - } - - if (portOutput) { - return { - isInUse: true, - conflictingContainer: containerName, - }; - } - } - - // Method 2: Check using ss/netstat for any process using the port - const portCheckCommand = `ss -tuln 2>/dev/null | grep ':${port} ' || netstat -tuln 2>/dev/null | grep ':${port} ' || true`; - let portCheckOutput = ""; - if (serverId) { - const result = await execAsyncRemote(serverId, portCheckCommand); - portCheckOutput = result.stdout.trim(); - } else { - const result = await execAsync(portCheckCommand); - portCheckOutput = result.stdout.trim(); - } - - if (portCheckOutput) { - // Port is in use but we couldn't identify the container - // This could be a non-Docker process or a container we couldn't detect - return { isInUse: true }; - } - - return { isInUse: false }; + return { + isInUse: !!container, + conflictingContainer: container || undefined, + }; } catch (error) { - // If check fails, log error but don't block the operation - // The actual Docker bind will fail if port is truly in use console.error("Error checking port availability:", error); return { isInUse: false }; }