fix(traefik): validate port 8080 before enabling dashboard

This commit is contained in:
HarikrishnanD
2025-11-13 11:52:06 +05:30
parent fd8f0e8f1f
commit c459997453
4 changed files with 93 additions and 4 deletions

View File

@@ -97,7 +97,12 @@ export const ShowTraefikActions = ({ serverId }: Props) => {
);
refetchDashboard();
})
.catch(() => {});
.catch((error) => {
const errorMessage =
error?.message ||
"Failed to toggle dashboard. Please check if port 8080 is available.";
toast.error(errorMessage);
});
}}
className="w-full cursor-pointer space-x-3"
>

View File

@@ -1,6 +1,7 @@
import {
canAccessToTraefikFiles,
checkGPUStatus,
checkPortInUse,
cleanStoppedContainers,
cleanUpDockerBuilder,
cleanUpSystemPrune,
@@ -130,6 +131,17 @@ export const settingsRouter = createTRPCRouter({
let newPorts = ports;
// If receive true, add 8080 to ports
if (input.enableDashboard) {
// 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}"`
: "";
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.`,
});
}
newPorts.push({
targetPort: 8080,
publishedPort: 8080,

View File

@@ -290,10 +290,10 @@ export const getContainersByAppLabel = async (
const command =
type === "swarm"
? `docker ps --filter "label=com.docker.swarm.service.name=${appName}" --format 'CONTAINER ID : {{.ID}} | Name: {{.Names}} | State: {{.State}}'`
? `docker ps -a --filter "label=com.docker.swarm.service.name=${appName}" --format 'CONTAINER ID : {{.ID}} | Name: {{.Names}} | State: {{.State}}'`
: type === "standalone"
? `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}}'`;
? `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}}'`;
if (serverId) {
const result = await execAsyncRemote(serverId, command);
stdout = result.stdout;

View File

@@ -394,6 +394,78 @@ 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 containerNames = containersList
.split("\n")
.filter((name) => name && name !== "dokploy-traefik");
// 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 };
} 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 };
}
};
export const writeTraefikSetup = async (input: TraefikOptions) => {
const resourceType = await getDockerResourceType(
"dokploy-traefik",