From 203da1a8fe25a5c2ab66738cf07a0c7e3b768427 Mon Sep 17 00:00:00 2001
From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com>
Date: Tue, 25 Feb 2025 22:51:02 -0600
Subject: [PATCH 1/9] refactor(traefik): migrate from Docker Swarm service to
standalone container
---
packages/server/src/setup/traefik-setup.ts | 143 +++++++++------------
1 file changed, 59 insertions(+), 84 deletions(-)
diff --git a/packages/server/src/setup/traefik-setup.ts b/packages/server/src/setup/traefik-setup.ts
index 21caa5cf2..5a904eb89 100644
--- a/packages/server/src/setup/traefik-setup.ts
+++ b/packages/server/src/setup/traefik-setup.ts
@@ -1,6 +1,6 @@
import { chmodSync, existsSync, mkdirSync, writeFileSync } from "node:fs";
import path from "node:path";
-import type { ContainerTaskSpec, CreateServiceOptions } from "dockerode";
+import type { ContainerCreateOptions } from "dockerode";
import { dump } from "js-yaml";
import { paths } from "../constants";
import { pullImage, pullRemoteImage } from "../utils/docker/utils";
@@ -34,68 +34,53 @@ export const initializeTraefik = async ({
const { MAIN_TRAEFIK_PATH, DYNAMIC_TRAEFIK_PATH } = paths(!!serverId);
const imageName = `traefik:v${TRAEFIK_VERSION}`;
const containerName = "dokploy-traefik";
- const settings: CreateServiceOptions = {
- Name: containerName,
- TaskTemplate: {
- ContainerSpec: {
- Image: imageName,
- Env: env,
- Mounts: [
+ const settings: ContainerCreateOptions = {
+ name: containerName,
+ Image: imageName,
+ NetworkingConfig: {
+ EndpointsConfig: {
+ "dokploy-network": {},
+ },
+ },
+ HostConfig: {
+ RestartPolicy: {
+ Name: "always",
+ },
+ Binds: [
+ `${MAIN_TRAEFIK_PATH}/traefik.yml:/etc/traefik/traefik.yml`,
+ `${DYNAMIC_TRAEFIK_PATH}:/etc/dokploy/traefik/dynamic`,
+ "/var/run/docker.sock:/var/run/docker.sock",
+ ],
+ PortBindings: {
+ [`${TRAEFIK_SSL_PORT}/tcp`]: [
{
- Type: "bind",
- Source: `${MAIN_TRAEFIK_PATH}/traefik.yml`,
- Target: "/etc/traefik/traefik.yml",
- },
- {
- Type: "bind",
- Source: DYNAMIC_TRAEFIK_PATH,
- Target: "/etc/dokploy/traefik/dynamic",
- },
- {
- Type: "bind",
- Source: "/var/run/docker.sock",
- Target: "/var/run/docker.sock",
+ HostPort: TRAEFIK_SSL_PORT.toString(),
},
],
- },
- Networks: [{ Target: "dokploy-network" }],
- Placement: {
- Constraints: ["node.role==manager"],
- },
- },
- Mode: {
- Replicated: {
- Replicas: 1,
- },
- },
- EndpointSpec: {
- Ports: [
- {
- TargetPort: 443,
- PublishedPort: TRAEFIK_SSL_PORT,
- PublishMode: "host",
- },
- {
- TargetPort: 80,
- PublishedPort: TRAEFIK_PORT,
- PublishMode: "host",
- },
- ...(enableDashboard
- ? [
+ [`${TRAEFIK_PORT}/tcp`]: [
+ {
+ HostPort: TRAEFIK_PORT.toString(),
+ },
+ ],
+ ...(enableDashboard && {
+ [`${8080}/tcp`]: [
+ {
+ HostPort: "8080",
+ },
+ ],
+ }),
+ ...additionalPorts.map((port) => {
+ return {
+ [`${port.targetPort}/tcp`]: [
{
- TargetPort: 8080,
- PublishedPort: 8080,
- PublishMode: "host" as const,
+ HostPort: port.publishedPort.toString(),
},
- ]
- : []),
- ...additionalPorts.map((port) => ({
- TargetPort: port.targetPort,
- PublishedPort: port.publishedPort,
- PublishMode: port.publishMode || ("host" as const),
- })),
- ],
+ ],
+ };
+ }),
+ },
},
+ Env: env,
};
const docker = await getRemoteDocker(serverId);
try {
@@ -105,38 +90,28 @@ export const initializeTraefik = async ({
await pullImage(imageName);
}
- const service = docker.getService(containerName);
- const inspect = await service.inspect();
+ const container = docker.getContainer(containerName);
+ try {
+ const inspect = await container.inspect();
+ if (inspect.State.Status === "running") {
+ console.log("Traefik already running");
+ return;
+ }
- const existingEnv = inspect.Spec.TaskTemplate.ContainerSpec.Env || [];
- const updatedEnv = !env ? existingEnv : env;
+ await container.remove({ force: true });
+ console.log("Removed existing container");
+ } catch (error) {
+ console.log("Traefik Not Found: Starting1 ✅");
+ console.log(error);
+ }
- const updatedSettings = {
- ...settings,
- TaskTemplate: {
- ...settings.TaskTemplate,
- ContainerSpec: {
- ...(settings?.TaskTemplate as ContainerTaskSpec).ContainerSpec,
- Env: updatedEnv,
- },
- },
- };
- await service.update({
- version: Number.parseInt(inspect.Version.Index),
- ...updatedSettings,
- });
+ await docker.createContainer(settings);
+ const newContainer = docker.getContainer(containerName);
+ await newContainer.start();
console.log("Traefik Started ✅");
} catch (error) {
- try {
- await docker.createService(settings);
- } catch (error: any) {
- if (error?.statusCode !== 409) {
- throw error;
- }
- console.log("Traefik service already exists, continuing...");
- }
- console.log("Traefik Not Found: Starting ✅");
+ console.log("Traefik Not Found: Starting2 ✅", error);
}
};
From 29c1e4691ea9ed41b01c20ba8981224ea147ba72 Mon Sep 17 00:00:00 2001
From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com>
Date: Tue, 25 Feb 2025 23:04:19 -0600
Subject: [PATCH 2/9] feat(docker): add support for standalone container log
retrieval
---
.../settings/servers/actions/show-traefik-actions.tsx | 6 +++++-
.../dashboard/settings/web-server/show-modal-logs.tsx | 9 ++++++++-
apps/dokploy/server/api/routers/docker.ts | 7 ++++++-
packages/server/src/services/docker.ts | 8 +++++++-
4 files changed, 26 insertions(+), 4 deletions(-)
diff --git a/apps/dokploy/components/dashboard/settings/servers/actions/show-traefik-actions.tsx b/apps/dokploy/components/dashboard/settings/servers/actions/show-traefik-actions.tsx
index 17a6ae757..f9b799476 100644
--- a/apps/dokploy/components/dashboard/settings/servers/actions/show-traefik-actions.tsx
+++ b/apps/dokploy/components/dashboard/settings/servers/actions/show-traefik-actions.tsx
@@ -79,7 +79,11 @@ export const ShowTraefikActions = ({ serverId }: Props) => {
>
{t("settings.server.webServer.reload")}
-
+
e.preventDefault()}
className="cursor-pointer"
diff --git a/apps/dokploy/components/dashboard/settings/web-server/show-modal-logs.tsx b/apps/dokploy/components/dashboard/settings/web-server/show-modal-logs.tsx
index 12e7b6704..f6d17f9b2 100644
--- a/apps/dokploy/components/dashboard/settings/web-server/show-modal-logs.tsx
+++ b/apps/dokploy/components/dashboard/settings/web-server/show-modal-logs.tsx
@@ -37,13 +37,20 @@ interface Props {
appName: string;
children?: React.ReactNode;
serverId?: string;
+ type: "standalone" | "swarm";
}
-export const ShowModalLogs = ({ appName, children, serverId }: Props) => {
+export const ShowModalLogs = ({
+ appName,
+ children,
+ serverId,
+ type = "swarm",
+}: Props) => {
const { data, isLoading } = api.docker.getContainersByAppLabel.useQuery(
{
appName,
serverId,
+ type,
},
{
enabled: !!appName,
diff --git a/apps/dokploy/server/api/routers/docker.ts b/apps/dokploy/server/api/routers/docker.ts
index f6972e16b..3de59058c 100644
--- a/apps/dokploy/server/api/routers/docker.ts
+++ b/apps/dokploy/server/api/routers/docker.ts
@@ -65,10 +65,15 @@ export const dockerRouter = createTRPCRouter({
z.object({
appName: z.string().min(1),
serverId: z.string().optional(),
+ type: z.enum(["standalone", "swarm"]),
}),
)
.query(async ({ input }) => {
- return await getContainersByAppLabel(input.appName, input.serverId);
+ return await getContainersByAppLabel(
+ input.appName,
+ input.type,
+ input.serverId,
+ );
}),
getStackContainersByAppName: protectedProcedure
diff --git a/packages/server/src/services/docker.ts b/packages/server/src/services/docker.ts
index 597c03fa1..fbb51192a 100644
--- a/packages/server/src/services/docker.ts
+++ b/packages/server/src/services/docker.ts
@@ -281,13 +281,19 @@ export const getServiceContainersByAppName = async (
export const getContainersByAppLabel = async (
appName: string,
+ type: "standalone" | "swarm",
serverId?: string,
) => {
try {
let stdout = "";
let stderr = "";
- const command = `docker ps --filter "label=com.docker.swarm.service.name=${appName}" --format 'CONTAINER ID : {{.ID}} | Name: {{.Names}} | State: {{.State}}'`;
+ const command =
+ type === "swarm"
+ ? `docker ps --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}}'`;
if (serverId) {
const result = await execAsyncRemote(serverId, command);
stdout = result.stdout;
From 7f8f6ac64cd83e45e50cac1528cd5b4464bcc18f Mon Sep 17 00:00:00 2001
From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com>
Date: Sun, 2 Mar 2025 03:14:54 -0600
Subject: [PATCH 3/9] refactor(traefik): migrate from Docker Swarm to
standalone container
- Replace Docker service commands with standalone container management
- Update Traefik initialization to use container-based deployment
- Modify port inspection and environment variable retrieval methods
- Improve container creation and port binding logic
- Remove Swarm-specific constraints and deployment strategies
---
.../servers/actions/show-traefik-actions.tsx | 9 --
apps/dokploy/server/api/routers/settings.ts | 95 +++++++++++--------
packages/server/src/setup/server-setup.ts | 19 ++--
packages/server/src/setup/traefik-setup.ts | 58 +++++------
4 files changed, 91 insertions(+), 90 deletions(-)
diff --git a/apps/dokploy/components/dashboard/settings/servers/actions/show-traefik-actions.tsx b/apps/dokploy/components/dashboard/settings/servers/actions/show-traefik-actions.tsx
index bd168edcd..b43686bde 100644
--- a/apps/dokploy/components/dashboard/settings/servers/actions/show-traefik-actions.tsx
+++ b/apps/dokploy/components/dashboard/settings/servers/actions/show-traefik-actions.tsx
@@ -112,15 +112,6 @@ export const ShowTraefikActions = ({ serverId }: Props) => {
{haveTraefikDashboardPortEnabled ? "Disable" : "Enable"} Dashboard
- {/*
-
- e.preventDefault()}
- >
- Enter the terminal
-
- */}
e.preventDefault()}
diff --git a/apps/dokploy/server/api/routers/settings.ts b/apps/dokploy/server/api/routers/settings.ts
index fc1255fcf..03711fe15 100644
--- a/apps/dokploy/server/api/routers/settings.ts
+++ b/apps/dokploy/server/api/routers/settings.ts
@@ -42,10 +42,6 @@ import {
recreateDirectory,
sendDockerCleanupNotifications,
spawnAsync,
- startService,
- startServiceRemote,
- stopService,
- stopServiceRemote,
updateLetsEncryptEmail,
updateServerById,
updateServerTraefik,
@@ -86,11 +82,9 @@ export const settingsRouter = createTRPCRouter({
.mutation(async ({ input }) => {
try {
if (input?.serverId) {
- await stopServiceRemote(input.serverId, "dokploy-traefik");
- await startServiceRemote(input.serverId, "dokploy-traefik");
+ await execAsync("docker restart dokploy-traefik");
} else if (!IS_CLOUD) {
- await stopService("dokploy-traefik");
- await startService("dokploy-traefik");
+ await execAsync("docker restart dokploy-traefik");
}
} catch (err) {
console.error(err);
@@ -104,6 +98,7 @@ export const settingsRouter = createTRPCRouter({
await initializeTraefik({
enableDashboard: input.enableDashboard,
serverId: input.serverId,
+ force: true,
});
return true;
}),
@@ -511,16 +506,18 @@ export const settingsRouter = createTRPCRouter({
.input(apiServerSchema)
.query(async ({ input }) => {
const command =
- "docker service inspect --format='{{range .Spec.TaskTemplate.ContainerSpec.Env}}{{println .}}{{end}}' dokploy-traefik";
+ "docker container inspect dokploy-traefik --format '{{json .Config.Env}}'";
+ let result = "";
if (input?.serverId) {
- const result = await execAsyncRemote(input.serverId, command);
- return result.stdout.trim();
- }
- if (!IS_CLOUD) {
- const result = await execAsync(command);
- return result.stdout.trim();
+ const execResult = await execAsyncRemote(input.serverId, command);
+ result = execResult.stdout;
+ } else {
+ const execResult = await execAsync(command);
+ result = execResult.stdout;
}
+ const envVars = JSON.parse(result.trim());
+ return envVars.join("\n");
}),
writeTraefikEnv: adminProcedure
@@ -530,6 +527,7 @@ export const settingsRouter = createTRPCRouter({
await initializeTraefik({
env: envs,
serverId: input.serverId,
+ force: true,
});
return true;
@@ -537,27 +535,22 @@ export const settingsRouter = createTRPCRouter({
haveTraefikDashboardPortEnabled: adminProcedure
.input(apiServerSchema)
.query(async ({ input }) => {
- const command = `docker service inspect --format='{{json .Endpoint.Ports}}' dokploy-traefik`;
+ const command = `docker container inspect --format='{{json .NetworkSettings.Ports}}' dokploy-traefik`;
let stdout = "";
if (input?.serverId) {
const result = await execAsyncRemote(input.serverId, command);
stdout = result.stdout;
} else if (!IS_CLOUD) {
- const result = await execAsync(
- "docker service inspect --format='{{json .Endpoint.Ports}}' dokploy-traefik",
- );
+ const result = await execAsync(command);
stdout = result.stdout;
}
- const parsed: any[] = JSON.parse(stdout.trim());
- for (const port of parsed) {
- if (port.PublishedPort === 8080) {
- return true;
- }
- }
-
- return false;
+ const ports = JSON.parse(stdout.trim());
+ return Object.entries(ports).some(([containerPort, bindings]) => {
+ const [port] = containerPort.split("/");
+ return port === "8080" && bindings && (bindings as any[]).length > 0;
+ });
}),
readStatsLogs: adminProcedure
@@ -767,6 +760,7 @@ export const settingsRouter = createTRPCRouter({
await initializeTraefik({
serverId: input.serverId,
additionalPorts: input.additionalPorts,
+ force: true,
});
return true;
} catch (error) {
@@ -783,7 +777,7 @@ export const settingsRouter = createTRPCRouter({
getTraefikPorts: adminProcedure
.input(apiServerSchema)
.query(async ({ input }) => {
- const command = `docker service inspect --format='{{json .Endpoint.Ports}}' dokploy-traefik`;
+ const command = `docker container inspect --format='{{json .NetworkSettings.Ports}}' dokploy-traefik`;
try {
let stdout = "";
@@ -795,21 +789,38 @@ export const settingsRouter = createTRPCRouter({
stdout = result.stdout;
}
- const ports: {
- Protocol: string;
- TargetPort: number;
- PublishedPort: number;
- PublishMode: string;
- }[] = JSON.parse(stdout.trim());
+ const portsMap = JSON.parse(stdout.trim());
+ const additionalPorts: Array<{
+ targetPort: number;
+ publishedPort: number;
+ publishMode: "host" | "ingress";
+ }> = [];
- // Filter out the default ports (80, 443, and optionally 8080)
- const additionalPorts = ports
- .filter((port) => ![80, 443, 8080].includes(port.PublishedPort))
- .map((port) => ({
- targetPort: port.TargetPort,
- publishedPort: port.PublishedPort,
- publishMode: port.PublishMode.toLowerCase() as "host" | "ingress",
- }));
+ // Convert the Docker container port format to our expected format
+ for (const [containerPort, bindings] of Object.entries(portsMap)) {
+ if (!bindings) continue;
+
+ const [port = ""] = containerPort.split("/");
+ if (!port) continue;
+
+ const targetPortNum = Number.parseInt(port, 10);
+ if (Number.isNaN(targetPortNum)) continue;
+
+ // Skip default ports
+ if ([80, 443, 8080].includes(targetPortNum)) continue;
+
+ for (const binding of bindings as Array<{ HostPort: string }>) {
+ if (!binding.HostPort) continue;
+ const publishedPort = Number.parseInt(binding.HostPort, 10);
+ if (Number.isNaN(publishedPort)) continue;
+
+ additionalPorts.push({
+ targetPort: targetPortNum,
+ publishedPort,
+ publishMode: "host", // Docker standalone uses host mode by default
+ });
+ }
+ }
return additionalPorts;
} catch (error) {
diff --git a/packages/server/src/setup/server-setup.ts b/packages/server/src/setup/server-setup.ts
index dc760407e..0e2a18117 100644
--- a/packages/server/src/setup/server-setup.ts
+++ b/packages/server/src/setup/server-setup.ts
@@ -539,22 +539,21 @@ export const installRClone = () => `
export const createTraefikInstance = () => {
const command = `
# Check if dokpyloy-traefik exists
- if docker service ls | grep -q 'dokploy-traefik'; then
+ if docker ps -a --format '{{.Names}}' | grep -q '^dokploy-traefik$'; then
echo "Traefik already exists ✅"
else
- # Create the dokploy-traefik service
+ # Create the dokploy-traefik container
TRAEFIK_VERSION=${TRAEFIK_VERSION}
- docker service create \
+ docker run -d \
--name dokploy-traefik \
- --replicas 1 \
- --constraint 'node.role==manager' \
--network dokploy-network \
- --mount type=bind,src=/etc/dokploy/traefik/traefik.yml,dst=/etc/traefik/traefik.yml \
- --mount type=bind,src=/etc/dokploy/traefik/dynamic,dst=/etc/dokploy/traefik/dynamic \
- --mount type=bind,src=/var/run/docker.sock,dst=/var/run/docker.sock \
+ --restart unless-stopped \
+ -v /etc/dokploy/traefik/traefik.yml:/etc/traefik/traefik.yml \
+ -v /etc/dokploy/traefik/dynamic:/etc/dokploy/traefik/dynamic \
+ -v /var/run/docker.sock:/var/run/docker.sock \
--label traefik.enable=true \
- --publish mode=host,target=${TRAEFIK_SSL_PORT},published=${TRAEFIK_SSL_PORT} \
- --publish mode=host,target=${TRAEFIK_PORT},published=${TRAEFIK_PORT} \
+ -p ${TRAEFIK_SSL_PORT}:${TRAEFIK_SSL_PORT} \
+ -p ${TRAEFIK_PORT}:${TRAEFIK_PORT} \
traefik:v$TRAEFIK_VERSION
echo "Traefik version $TRAEFIK_VERSION installed ✅"
fi
diff --git a/packages/server/src/setup/traefik-setup.ts b/packages/server/src/setup/traefik-setup.ts
index 5a904eb89..73ebaa757 100644
--- a/packages/server/src/setup/traefik-setup.ts
+++ b/packages/server/src/setup/traefik-setup.ts
@@ -23,6 +23,7 @@ interface TraefikOptions {
publishedPort: number;
publishMode?: "ingress" | "host";
}[];
+ force?: boolean;
}
export const initializeTraefik = async ({
@@ -30,10 +31,33 @@ export const initializeTraefik = async ({
env,
serverId,
additionalPorts = [],
+ force = false,
}: TraefikOptions = {}) => {
const { MAIN_TRAEFIK_PATH, DYNAMIC_TRAEFIK_PATH } = paths(!!serverId);
const imageName = `traefik:v${TRAEFIK_VERSION}`;
const containerName = "dokploy-traefik";
+
+ const exposedPorts: Record = {
+ [`${TRAEFIK_PORT}/tcp`]: {},
+ [`${TRAEFIK_SSL_PORT}/tcp`]: {},
+ };
+
+ const portBindings: Record> = {
+ [`${TRAEFIK_PORT}/tcp`]: [{ HostPort: TRAEFIK_PORT.toString() }],
+ [`${TRAEFIK_SSL_PORT}/tcp`]: [{ HostPort: TRAEFIK_SSL_PORT.toString() }],
+ };
+
+ if (enableDashboard) {
+ exposedPorts["8080/tcp"] = {};
+ portBindings["8080/tcp"] = [{ HostPort: "8080" }];
+ }
+
+ for (const port of additionalPorts) {
+ const portKey = `${port.targetPort}/tcp`;
+ exposedPorts[portKey] = {};
+ portBindings[portKey] = [{ HostPort: port.publishedPort.toString() }];
+ }
+
const settings: ContainerCreateOptions = {
name: containerName,
Image: imageName,
@@ -42,6 +66,7 @@ export const initializeTraefik = async ({
"dokploy-network": {},
},
},
+ ExposedPorts: exposedPorts,
HostConfig: {
RestartPolicy: {
Name: "always",
@@ -51,37 +76,11 @@ export const initializeTraefik = async ({
`${DYNAMIC_TRAEFIK_PATH}:/etc/dokploy/traefik/dynamic`,
"/var/run/docker.sock:/var/run/docker.sock",
],
- PortBindings: {
- [`${TRAEFIK_SSL_PORT}/tcp`]: [
- {
- HostPort: TRAEFIK_SSL_PORT.toString(),
- },
- ],
- [`${TRAEFIK_PORT}/tcp`]: [
- {
- HostPort: TRAEFIK_PORT.toString(),
- },
- ],
- ...(enableDashboard && {
- [`${8080}/tcp`]: [
- {
- HostPort: "8080",
- },
- ],
- }),
- ...additionalPorts.map((port) => {
- return {
- [`${port.targetPort}/tcp`]: [
- {
- HostPort: port.publishedPort.toString(),
- },
- ],
- };
- }),
- },
+ PortBindings: portBindings,
},
Env: env,
};
+
const docker = await getRemoteDocker(serverId);
try {
if (serverId) {
@@ -93,7 +92,7 @@ export const initializeTraefik = async ({
const container = docker.getContainer(containerName);
try {
const inspect = await container.inspect();
- if (inspect.State.Status === "running") {
+ if (inspect.State.Status === "running" && !force) {
console.log("Traefik already running");
return;
}
@@ -112,6 +111,7 @@ export const initializeTraefik = async ({
console.log("Traefik Started ✅");
} catch (error) {
console.log("Traefik Not Found: Starting2 ✅", error);
+ throw error;
}
};
From 2ae14c65cfa8d832db115b8fde3de15ec5e81d88 Mon Sep 17 00:00:00 2001
From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com>
Date: Sun, 2 Mar 2025 03:24:29 -0600
Subject: [PATCH 4/9] refactor(web-server): make type prop optional in
ShowModalLogs component
- Update type prop to be optional in the Props interface
- Improve component flexibility by allowing undefined type
---
.../dashboard/settings/web-server/show-modal-logs.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/apps/dokploy/components/dashboard/settings/web-server/show-modal-logs.tsx b/apps/dokploy/components/dashboard/settings/web-server/show-modal-logs.tsx
index 677db9060..43b1838de 100644
--- a/apps/dokploy/components/dashboard/settings/web-server/show-modal-logs.tsx
+++ b/apps/dokploy/components/dashboard/settings/web-server/show-modal-logs.tsx
@@ -36,7 +36,7 @@ interface Props {
appName: string;
children?: React.ReactNode;
serverId?: string;
- type: "standalone" | "swarm";
+ type?: "standalone" | "swarm";
}
export const ShowModalLogs = ({
From d2e053635558ca7b9b914a2c01b8d3d5a91f97a8 Mon Sep 17 00:00:00 2001
From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com>
Date: Sun, 2 Mar 2025 04:31:06 -0600
Subject: [PATCH 5/9] feat(traefik): add HTTP/3 support with UDP port
configuration
- Introduce TRAEFIK_HTTP3_PORT environment variable
- Configure UDP port binding for HTTP/3
- Enable HTTP/3 with advertisedPort in Traefik websecure configuration
---
packages/server/src/setup/traefik-setup.ts | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/packages/server/src/setup/traefik-setup.ts b/packages/server/src/setup/traefik-setup.ts
index 73ebaa757..c2e8a6159 100644
--- a/packages/server/src/setup/traefik-setup.ts
+++ b/packages/server/src/setup/traefik-setup.ts
@@ -12,6 +12,8 @@ export const TRAEFIK_SSL_PORT =
Number.parseInt(process.env.TRAEFIK_SSL_PORT!, 10) || 443;
export const TRAEFIK_PORT =
Number.parseInt(process.env.TRAEFIK_PORT!, 10) || 80;
+export const TRAEFIK_HTTP3_PORT =
+ Number.parseInt(process.env.TRAEFIK_HTTP3_PORT!, 10) || 443;
export const TRAEFIK_VERSION = process.env.TRAEFIK_VERSION || "3.1.2";
interface TraefikOptions {
@@ -40,11 +42,15 @@ export const initializeTraefik = async ({
const exposedPorts: Record = {
[`${TRAEFIK_PORT}/tcp`]: {},
[`${TRAEFIK_SSL_PORT}/tcp`]: {},
+ [`${TRAEFIK_HTTP3_PORT}/udp`]: {},
};
const portBindings: Record> = {
[`${TRAEFIK_PORT}/tcp`]: [{ HostPort: TRAEFIK_PORT.toString() }],
[`${TRAEFIK_SSL_PORT}/tcp`]: [{ HostPort: TRAEFIK_SSL_PORT.toString() }],
+ [`${TRAEFIK_HTTP3_PORT}/udp`]: [
+ { HostPort: TRAEFIK_HTTP3_PORT.toString() },
+ ],
};
if (enableDashboard) {
@@ -187,6 +193,9 @@ export const getDefaultTraefikConfig = () => {
},
websecure: {
address: `:${TRAEFIK_SSL_PORT}`,
+ http3: {
+ advertisedPort: TRAEFIK_HTTP3_PORT,
+ },
...(process.env.NODE_ENV === "production" && {
http: {
tls: {
From bf04dfa757ca6474b1d5381c30b41e9a06cd8ed0 Mon Sep 17 00:00:00 2001
From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com>
Date: Sun, 2 Mar 2025 04:35:58 -0600
Subject: [PATCH 6/9] feat(traefik): add HTTP/3 support with UDP port
configuration
- Introduce TRAEFIK_HTTP3_PORT environment variable
- Configure UDP port binding for HTTP/3
- Enable HTTP/3 with advertisedPort in Traefik websecure configuration
---
packages/server/src/setup/server-setup.ts | 3 ++-
packages/server/src/setup/traefik-setup.ts | 3 +++
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/packages/server/src/setup/server-setup.ts b/packages/server/src/setup/server-setup.ts
index 0e2a18117..fde7a1a8b 100644
--- a/packages/server/src/setup/server-setup.ts
+++ b/packages/server/src/setup/server-setup.ts
@@ -9,6 +9,7 @@ import {
TRAEFIK_PORT,
TRAEFIK_SSL_PORT,
TRAEFIK_VERSION,
+ TRAEFIK_HTTP3_PORT,
getDefaultMiddlewares,
getDefaultServerTraefikConfig,
} from "@dokploy/server/setup/traefik-setup";
@@ -551,9 +552,9 @@ export const createTraefikInstance = () => {
-v /etc/dokploy/traefik/traefik.yml:/etc/traefik/traefik.yml \
-v /etc/dokploy/traefik/dynamic:/etc/dokploy/traefik/dynamic \
-v /var/run/docker.sock:/var/run/docker.sock \
- --label traefik.enable=true \
-p ${TRAEFIK_SSL_PORT}:${TRAEFIK_SSL_PORT} \
-p ${TRAEFIK_PORT}:${TRAEFIK_PORT} \
+ -p ${TRAEFIK_HTTP3_PORT}:${TRAEFIK_HTTP3_PORT}/udp \
traefik:v$TRAEFIK_VERSION
echo "Traefik version $TRAEFIK_VERSION installed ✅"
fi
diff --git a/packages/server/src/setup/traefik-setup.ts b/packages/server/src/setup/traefik-setup.ts
index c2e8a6159..1778d319f 100644
--- a/packages/server/src/setup/traefik-setup.ts
+++ b/packages/server/src/setup/traefik-setup.ts
@@ -251,6 +251,9 @@ export const getDefaultServerTraefikConfig = () => {
},
websecure: {
address: `:${TRAEFIK_SSL_PORT}`,
+ http3: {
+ advertisedPort: TRAEFIK_HTTP3_PORT,
+ },
http: {
tls: {
certResolver: "letsencrypt",
From 8063673a7c9c59c16ce9ff14a29579c04b10ebfa Mon Sep 17 00:00:00 2001
From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com>
Date: Sun, 2 Mar 2025 05:17:42 -0600
Subject: [PATCH 7/9] refactor(traefik): streamline container removal and
service management
- Remove dokploy-service before Traefik container initialization
- Simplify error handling and logging during container setup
- Add support for remote server service removal
---
packages/server/src/setup/traefik-setup.ts | 14 +++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)
diff --git a/packages/server/src/setup/traefik-setup.ts b/packages/server/src/setup/traefik-setup.ts
index 1778d319f..7f124234b 100644
--- a/packages/server/src/setup/traefik-setup.ts
+++ b/packages/server/src/setup/traefik-setup.ts
@@ -7,6 +7,7 @@ import { pullImage, pullRemoteImage } from "../utils/docker/utils";
import { getRemoteDocker } from "../utils/servers/remote-docker";
import type { FileConfig } from "../utils/traefik/file-types";
import type { MainTraefikConfig } from "../utils/traefik/types";
+import { execAsync, execAsyncRemote } from "../utils/process/execAsync";
export const TRAEFIK_SSL_PORT =
Number.parseInt(process.env.TRAEFIK_SSL_PORT!, 10) || 443;
@@ -95,6 +96,14 @@ export const initializeTraefik = async ({
await pullImage(imageName);
}
+ // remove dokploy-service if it exists
+ const command = "docker service rm dokploy-service > /dev/null 2>&1";
+ if (serverId) {
+ await execAsyncRemote(command, serverId);
+ } else {
+ await execAsync(command);
+ }
+
const container = docker.getContainer(containerName);
try {
const inspect = await container.inspect();
@@ -104,19 +113,14 @@ export const initializeTraefik = async ({
}
await container.remove({ force: true });
- console.log("Removed existing container");
} catch (error) {
- console.log("Traefik Not Found: Starting1 ✅");
console.log(error);
}
await docker.createContainer(settings);
const newContainer = docker.getContainer(containerName);
await newContainer.start();
-
- console.log("Traefik Started ✅");
} catch (error) {
- console.log("Traefik Not Found: Starting2 ✅", error);
throw error;
}
};
From 988e5cb23eaf44f7b3fb6cdb462d77640e637c76 Mon Sep 17 00:00:00 2001
From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com>
Date: Sun, 9 Mar 2025 01:14:45 -0600
Subject: [PATCH 8/9] fix(traefik): improve error handling in container startup
Log Traefik container startup errors instead of throwing, preventing potential deployment interruptions
---
packages/server/src/setup/traefik-setup.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/server/src/setup/traefik-setup.ts b/packages/server/src/setup/traefik-setup.ts
index 7f124234b..c76a1f80e 100644
--- a/packages/server/src/setup/traefik-setup.ts
+++ b/packages/server/src/setup/traefik-setup.ts
@@ -121,7 +121,7 @@ export const initializeTraefik = async ({
const newContainer = docker.getContainer(containerName);
await newContainer.start();
} catch (error) {
- throw error;
+ console.log(error);
}
};
From b7f63fdad438839a42263374492b00692873ad4c Mon Sep 17 00:00:00 2001
From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com>
Date: Sun, 9 Mar 2025 02:32:02 -0600
Subject: [PATCH 9/9] refactor(traefik): improve migration and removal of
Traefik services
- Update Traefik service detection and removal logic in server and traefik setup
- Use Docker service and container inspection methods for more robust service management
- Add graceful migration and removal of existing Traefik services
- Simplify image pulling and service removal process
---
packages/server/src/setup/server-setup.ts | 9 ++++++++-
packages/server/src/setup/traefik-setup.ts | 20 +++++---------------
2 files changed, 13 insertions(+), 16 deletions(-)
diff --git a/packages/server/src/setup/server-setup.ts b/packages/server/src/setup/server-setup.ts
index 24cf76b9a..e129dce10 100644
--- a/packages/server/src/setup/server-setup.ts
+++ b/packages/server/src/setup/server-setup.ts
@@ -543,7 +543,14 @@ export const installRClone = () => `
export const createTraefikInstance = () => {
const command = `
# Check if dokpyloy-traefik exists
- if docker ps -a --format '{{.Names}}' | grep -q '^dokploy-traefik$'; then
+ if docker service inspect dokploy-traefik > /dev/null 2>&1; then
+ echo "Migrating Traefik to Standalone..."
+ docker service rm dokploy-traefik
+ sleep 7
+ echo "Traefik migrated to Standalone ✅"
+ fi
+
+ if docker inspect dokploy-traefik > /dev/null 2>&1; then
echo "Traefik already exists ✅"
else
# Create the dokploy-traefik container
diff --git a/packages/server/src/setup/traefik-setup.ts b/packages/server/src/setup/traefik-setup.ts
index c76a1f80e..7f2b707d0 100644
--- a/packages/server/src/setup/traefik-setup.ts
+++ b/packages/server/src/setup/traefik-setup.ts
@@ -3,11 +3,9 @@ import path from "node:path";
import type { ContainerCreateOptions } from "dockerode";
import { dump } from "js-yaml";
import { paths } from "../constants";
-import { pullImage, pullRemoteImage } from "../utils/docker/utils";
import { getRemoteDocker } from "../utils/servers/remote-docker";
import type { FileConfig } from "../utils/traefik/file-types";
import type { MainTraefikConfig } from "../utils/traefik/types";
-import { execAsync, execAsyncRemote } from "../utils/process/execAsync";
export const TRAEFIK_SSL_PORT =
Number.parseInt(process.env.TRAEFIK_SSL_PORT!, 10) || 443;
@@ -90,19 +88,11 @@ export const initializeTraefik = async ({
const docker = await getRemoteDocker(serverId);
try {
- if (serverId) {
- await pullRemoteImage(imageName, serverId);
- } else {
- await pullImage(imageName);
- }
-
- // remove dokploy-service if it exists
- const command = "docker service rm dokploy-service > /dev/null 2>&1";
- if (serverId) {
- await execAsyncRemote(command, serverId);
- } else {
- await execAsync(command);
- }
+ try {
+ const service = docker.getService("dokploy-traefik");
+ await service?.remove({ force: true });
+ await new Promise((resolve) => setTimeout(resolve, 5000));
+ } catch (_) {}
const container = docker.getContainer(containerName);
try {