From ce82e2322b8e721cee0988f7c7fa953acf7e7b09 Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Sun, 8 Mar 2026 03:08:38 -0600 Subject: [PATCH] fix: improve port conflict detection by enhancing error messages and adding host-level service checks --- apps/dokploy/server/api/routers/settings.ts | 6 ++-- packages/server/src/services/settings.ts | 39 ++++++++++++++++----- 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/apps/dokploy/server/api/routers/settings.ts b/apps/dokploy/server/api/routers/settings.ts index fee7f2f5d..30cb522ba 100644 --- a/apps/dokploy/server/api/routers/settings.ts +++ b/apps/dokploy/server/api/routers/settings.ts @@ -149,12 +149,12 @@ export const settingsRouter = createTRPCRouter({ // Check if port 8080 is already in use before enabling dashboard const portCheck = await checkPortInUse(8080, input.serverId); if (portCheck.isInUse) { - const conflictingContainer = portCheck.conflictingContainer - ? ` by container "${portCheck.conflictingContainer}"` + const conflictInfo = portCheck.conflictingContainer + ? ` by ${portCheck.conflictingContainer}` : ""; throw new TRPCError({ code: "CONFLICT", - message: `Port 8080 is already in use${conflictingContainer}. Please stop the conflicting service or use a different port for the Traefik dashboard.`, + message: `Port 8080 is already in use${conflictInfo}. Please stop the conflicting service or use a different port for the Traefik dashboard.`, }); } newPorts.push({ diff --git a/packages/server/src/services/settings.ts b/packages/server/src/services/settings.ts index f3603a8f0..07aaf690c 100644 --- a/packages/server/src/services/settings.ts +++ b/packages/server/src/services/settings.ts @@ -413,17 +413,38 @@ export const checkPortInUse = async ( serverId?: string, ): Promise<{ isInUse: boolean; conflictingContainer?: string }> => { try { - 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); + // Check if port is in use by a Docker container + const dockerCommand = `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: dockerOut } = serverId + ? await execAsyncRemote(serverId, dockerCommand) + : await execAsync(dockerCommand); - const container = stdout.trim(); + const container = dockerOut.trim(); - return { - isInUse: !!container, - conflictingContainer: container || undefined, - }; + if (container) { + return { + isInUse: true, + conflictingContainer: `container "${container}"`, + }; + } + + // Check if port is in use by a host-level service (non-Docker) + // Dokploy runs inside a container, so we spawn an ephemeral container + // with --net=host to share the host's network stack and use nc -z to + // check if something is listening on the port + const hostCommand = `docker run --rm --net=host busybox sh -c 'nc -z 0.0.0.0 ${port} 2>/dev/null && echo in_use || echo free'`; + const { stdout: hostOut } = serverId + ? await execAsyncRemote(serverId, hostCommand) + : await execAsync(hostCommand); + + if (hostOut.includes("in_use")) { + return { + isInUse: true, + conflictingContainer: "a host-level service", + }; + } + + return { isInUse: false }; } catch (error) { console.error("Error checking port availability:", error); return { isInUse: false };