mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
Merge pull request #1371 from Dokploy/1345-domain-not-working-after-server-restart-or-traefik-reload
refactor(traefik): migrate from Docker Swarm service to standalone co…
This commit is contained in:
@@ -67,7 +67,11 @@ export const ShowTraefikActions = ({ serverId }: Props) => {
|
|||||||
>
|
>
|
||||||
<span>{t("settings.server.webServer.reload")}</span>
|
<span>{t("settings.server.webServer.reload")}</span>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<ShowModalLogs appName="dokploy-traefik" serverId={serverId}>
|
<ShowModalLogs
|
||||||
|
appName="dokploy-traefik"
|
||||||
|
serverId={serverId}
|
||||||
|
type="standalone"
|
||||||
|
>
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
onSelect={(e) => e.preventDefault()}
|
onSelect={(e) => e.preventDefault()}
|
||||||
className="cursor-pointer"
|
className="cursor-pointer"
|
||||||
@@ -108,15 +112,6 @@ export const ShowTraefikActions = ({ serverId }: Props) => {
|
|||||||
{haveTraefikDashboardPortEnabled ? "Disable" : "Enable"} Dashboard
|
{haveTraefikDashboardPortEnabled ? "Disable" : "Enable"} Dashboard
|
||||||
</span>
|
</span>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
{/*
|
|
||||||
<DockerTerminalModal appName="dokploy-traefik">
|
|
||||||
<DropdownMenuItem
|
|
||||||
className="w-full cursor-pointer space-x-3"
|
|
||||||
onSelect={(e) => e.preventDefault()}
|
|
||||||
>
|
|
||||||
<span>Enter the terminal</span>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</DockerTerminalModal> */}
|
|
||||||
<ManageTraefikPorts serverId={serverId}>
|
<ManageTraefikPorts serverId={serverId}>
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
onSelect={(e) => e.preventDefault()}
|
onSelect={(e) => e.preventDefault()}
|
||||||
|
|||||||
@@ -36,13 +36,20 @@ interface Props {
|
|||||||
appName: string;
|
appName: string;
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
serverId?: string;
|
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(
|
const { data, isLoading } = api.docker.getContainersByAppLabel.useQuery(
|
||||||
{
|
{
|
||||||
appName,
|
appName,
|
||||||
serverId,
|
serverId,
|
||||||
|
type,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
enabled: !!appName,
|
enabled: !!appName,
|
||||||
|
|||||||
@@ -65,10 +65,15 @@ export const dockerRouter = createTRPCRouter({
|
|||||||
z.object({
|
z.object({
|
||||||
appName: z.string().min(1),
|
appName: z.string().min(1),
|
||||||
serverId: z.string().optional(),
|
serverId: z.string().optional(),
|
||||||
|
type: z.enum(["standalone", "swarm"]),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.query(async ({ input }) => {
|
.query(async ({ input }) => {
|
||||||
return await getContainersByAppLabel(input.appName, input.serverId);
|
return await getContainersByAppLabel(
|
||||||
|
input.appName,
|
||||||
|
input.type,
|
||||||
|
input.serverId,
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
|
|
||||||
getStackContainersByAppName: protectedProcedure
|
getStackContainersByAppName: protectedProcedure
|
||||||
|
|||||||
@@ -41,10 +41,6 @@ import {
|
|||||||
recreateDirectory,
|
recreateDirectory,
|
||||||
sendDockerCleanupNotifications,
|
sendDockerCleanupNotifications,
|
||||||
spawnAsync,
|
spawnAsync,
|
||||||
startService,
|
|
||||||
startServiceRemote,
|
|
||||||
stopService,
|
|
||||||
stopServiceRemote,
|
|
||||||
updateLetsEncryptEmail,
|
updateLetsEncryptEmail,
|
||||||
updateServerById,
|
updateServerById,
|
||||||
updateServerTraefik,
|
updateServerTraefik,
|
||||||
@@ -88,11 +84,9 @@ export const settingsRouter = createTRPCRouter({
|
|||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input }) => {
|
||||||
try {
|
try {
|
||||||
if (input?.serverId) {
|
if (input?.serverId) {
|
||||||
await stopServiceRemote(input.serverId, "dokploy-traefik");
|
await execAsync("docker restart dokploy-traefik");
|
||||||
await startServiceRemote(input.serverId, "dokploy-traefik");
|
|
||||||
} else if (!IS_CLOUD) {
|
} else if (!IS_CLOUD) {
|
||||||
await stopService("dokploy-traefik");
|
await execAsync("docker restart dokploy-traefik");
|
||||||
await startService("dokploy-traefik");
|
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
@@ -106,6 +100,7 @@ export const settingsRouter = createTRPCRouter({
|
|||||||
await initializeTraefik({
|
await initializeTraefik({
|
||||||
enableDashboard: input.enableDashboard,
|
enableDashboard: input.enableDashboard,
|
||||||
serverId: input.serverId,
|
serverId: input.serverId,
|
||||||
|
force: true,
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
}),
|
}),
|
||||||
@@ -513,16 +508,18 @@ export const settingsRouter = createTRPCRouter({
|
|||||||
.input(apiServerSchema)
|
.input(apiServerSchema)
|
||||||
.query(async ({ input }) => {
|
.query(async ({ input }) => {
|
||||||
const command =
|
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) {
|
if (input?.serverId) {
|
||||||
const result = await execAsyncRemote(input.serverId, command);
|
const execResult = await execAsyncRemote(input.serverId, command);
|
||||||
return result.stdout.trim();
|
result = execResult.stdout;
|
||||||
}
|
} else {
|
||||||
if (!IS_CLOUD) {
|
const execResult = await execAsync(command);
|
||||||
const result = await execAsync(command);
|
result = execResult.stdout;
|
||||||
return result.stdout.trim();
|
|
||||||
}
|
}
|
||||||
|
const envVars = JSON.parse(result.trim());
|
||||||
|
return envVars.join("\n");
|
||||||
}),
|
}),
|
||||||
|
|
||||||
writeTraefikEnv: adminProcedure
|
writeTraefikEnv: adminProcedure
|
||||||
@@ -532,6 +529,7 @@ export const settingsRouter = createTRPCRouter({
|
|||||||
await initializeTraefik({
|
await initializeTraefik({
|
||||||
env: envs,
|
env: envs,
|
||||||
serverId: input.serverId,
|
serverId: input.serverId,
|
||||||
|
force: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -539,27 +537,22 @@ export const settingsRouter = createTRPCRouter({
|
|||||||
haveTraefikDashboardPortEnabled: adminProcedure
|
haveTraefikDashboardPortEnabled: adminProcedure
|
||||||
.input(apiServerSchema)
|
.input(apiServerSchema)
|
||||||
.query(async ({ input }) => {
|
.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 = "";
|
let stdout = "";
|
||||||
if (input?.serverId) {
|
if (input?.serverId) {
|
||||||
const result = await execAsyncRemote(input.serverId, command);
|
const result = await execAsyncRemote(input.serverId, command);
|
||||||
stdout = result.stdout;
|
stdout = result.stdout;
|
||||||
} else if (!IS_CLOUD) {
|
} else if (!IS_CLOUD) {
|
||||||
const result = await execAsync(
|
const result = await execAsync(command);
|
||||||
"docker service inspect --format='{{json .Endpoint.Ports}}' dokploy-traefik",
|
|
||||||
);
|
|
||||||
stdout = result.stdout;
|
stdout = result.stdout;
|
||||||
}
|
}
|
||||||
|
|
||||||
const parsed: any[] = JSON.parse(stdout.trim());
|
const ports = JSON.parse(stdout.trim());
|
||||||
for (const port of parsed) {
|
return Object.entries(ports).some(([containerPort, bindings]) => {
|
||||||
if (port.PublishedPort === 8080) {
|
const [port] = containerPort.split("/");
|
||||||
return true;
|
return port === "8080" && bindings && (bindings as any[]).length > 0;
|
||||||
}
|
});
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
readStatsLogs: adminProcedure
|
readStatsLogs: adminProcedure
|
||||||
@@ -772,6 +765,7 @@ export const settingsRouter = createTRPCRouter({
|
|||||||
await initializeTraefik({
|
await initializeTraefik({
|
||||||
serverId: input.serverId,
|
serverId: input.serverId,
|
||||||
additionalPorts: input.additionalPorts,
|
additionalPorts: input.additionalPorts,
|
||||||
|
force: true,
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -788,7 +782,7 @@ export const settingsRouter = createTRPCRouter({
|
|||||||
getTraefikPorts: adminProcedure
|
getTraefikPorts: adminProcedure
|
||||||
.input(apiServerSchema)
|
.input(apiServerSchema)
|
||||||
.query(async ({ input }) => {
|
.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 {
|
try {
|
||||||
let stdout = "";
|
let stdout = "";
|
||||||
@@ -800,21 +794,38 @@ export const settingsRouter = createTRPCRouter({
|
|||||||
stdout = result.stdout;
|
stdout = result.stdout;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ports: {
|
const portsMap = JSON.parse(stdout.trim());
|
||||||
Protocol: string;
|
const additionalPorts: Array<{
|
||||||
TargetPort: number;
|
targetPort: number;
|
||||||
PublishedPort: number;
|
publishedPort: number;
|
||||||
PublishMode: string;
|
publishMode: "host" | "ingress";
|
||||||
}[] = JSON.parse(stdout.trim());
|
}> = [];
|
||||||
|
|
||||||
// Filter out the default ports (80, 443, and optionally 8080)
|
// Convert the Docker container port format to our expected format
|
||||||
const additionalPorts = ports
|
for (const [containerPort, bindings] of Object.entries(portsMap)) {
|
||||||
.filter((port) => ![80, 443, 8080].includes(port.PublishedPort))
|
if (!bindings) continue;
|
||||||
.map((port) => ({
|
|
||||||
targetPort: port.TargetPort,
|
const [port = ""] = containerPort.split("/");
|
||||||
publishedPort: port.PublishedPort,
|
if (!port) continue;
|
||||||
publishMode: port.PublishMode.toLowerCase() as "host" | "ingress",
|
|
||||||
}));
|
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;
|
return additionalPorts;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -287,13 +287,19 @@ export const getServiceContainersByAppName = async (
|
|||||||
|
|
||||||
export const getContainersByAppLabel = async (
|
export const getContainersByAppLabel = async (
|
||||||
appName: string,
|
appName: string,
|
||||||
|
type: "standalone" | "swarm",
|
||||||
serverId?: string,
|
serverId?: string,
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
let stdout = "";
|
let stdout = "";
|
||||||
let stderr = "";
|
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) {
|
if (serverId) {
|
||||||
const result = await execAsyncRemote(serverId, command);
|
const result = await execAsyncRemote(serverId, command);
|
||||||
stdout = result.stdout;
|
stdout = result.stdout;
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
TRAEFIK_PORT,
|
TRAEFIK_PORT,
|
||||||
TRAEFIK_SSL_PORT,
|
TRAEFIK_SSL_PORT,
|
||||||
TRAEFIK_VERSION,
|
TRAEFIK_VERSION,
|
||||||
|
TRAEFIK_HTTP3_PORT,
|
||||||
getDefaultMiddlewares,
|
getDefaultMiddlewares,
|
||||||
getDefaultServerTraefikConfig,
|
getDefaultServerTraefikConfig,
|
||||||
} from "@dokploy/server/setup/traefik-setup";
|
} from "@dokploy/server/setup/traefik-setup";
|
||||||
@@ -542,22 +543,28 @@ export const installRClone = () => `
|
|||||||
export const createTraefikInstance = () => {
|
export const createTraefikInstance = () => {
|
||||||
const command = `
|
const command = `
|
||||||
# Check if dokpyloy-traefik exists
|
# Check if dokpyloy-traefik exists
|
||||||
if docker service ls | 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 ✅"
|
echo "Traefik already exists ✅"
|
||||||
else
|
else
|
||||||
# Create the dokploy-traefik service
|
# Create the dokploy-traefik container
|
||||||
TRAEFIK_VERSION=${TRAEFIK_VERSION}
|
TRAEFIK_VERSION=${TRAEFIK_VERSION}
|
||||||
docker service create \
|
docker run -d \
|
||||||
--name dokploy-traefik \
|
--name dokploy-traefik \
|
||||||
--replicas 1 \
|
|
||||||
--constraint 'node.role==manager' \
|
|
||||||
--network dokploy-network \
|
--network dokploy-network \
|
||||||
--mount type=bind,src=/etc/dokploy/traefik/traefik.yml,dst=/etc/traefik/traefik.yml \
|
--restart unless-stopped \
|
||||||
--mount type=bind,src=/etc/dokploy/traefik/dynamic,dst=/etc/dokploy/traefik/dynamic \
|
-v /etc/dokploy/traefik/traefik.yml:/etc/traefik/traefik.yml \
|
||||||
--mount type=bind,src=/var/run/docker.sock,dst=/var/run/docker.sock \
|
-v /etc/dokploy/traefik/dynamic:/etc/dokploy/traefik/dynamic \
|
||||||
--label traefik.enable=true \
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||||
--publish mode=host,target=${TRAEFIK_SSL_PORT},published=${TRAEFIK_SSL_PORT} \
|
-p ${TRAEFIK_SSL_PORT}:${TRAEFIK_SSL_PORT} \
|
||||||
--publish mode=host,target=${TRAEFIK_PORT},published=${TRAEFIK_PORT} \
|
-p ${TRAEFIK_PORT}:${TRAEFIK_PORT} \
|
||||||
|
-p ${TRAEFIK_HTTP3_PORT}:${TRAEFIK_HTTP3_PORT}/udp \
|
||||||
traefik:v$TRAEFIK_VERSION
|
traefik:v$TRAEFIK_VERSION
|
||||||
echo "Traefik version $TRAEFIK_VERSION installed ✅"
|
echo "Traefik version $TRAEFIK_VERSION installed ✅"
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import { chmodSync, existsSync, mkdirSync, writeFileSync } from "node:fs";
|
import { chmodSync, existsSync, mkdirSync, writeFileSync } from "node:fs";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import type { ContainerTaskSpec, CreateServiceOptions } from "dockerode";
|
import type { ContainerCreateOptions } from "dockerode";
|
||||||
import { dump } from "js-yaml";
|
import { dump } from "js-yaml";
|
||||||
import { paths } from "../constants";
|
import { paths } from "../constants";
|
||||||
import { pullImage, pullRemoteImage } from "../utils/docker/utils";
|
|
||||||
import { getRemoteDocker } from "../utils/servers/remote-docker";
|
import { getRemoteDocker } from "../utils/servers/remote-docker";
|
||||||
import type { FileConfig } from "../utils/traefik/file-types";
|
import type { FileConfig } from "../utils/traefik/file-types";
|
||||||
import type { MainTraefikConfig } from "../utils/traefik/types";
|
import type { MainTraefikConfig } from "../utils/traefik/types";
|
||||||
@@ -12,6 +11,8 @@ export const TRAEFIK_SSL_PORT =
|
|||||||
Number.parseInt(process.env.TRAEFIK_SSL_PORT!, 10) || 443;
|
Number.parseInt(process.env.TRAEFIK_SSL_PORT!, 10) || 443;
|
||||||
export const TRAEFIK_PORT =
|
export const TRAEFIK_PORT =
|
||||||
Number.parseInt(process.env.TRAEFIK_PORT!, 10) || 80;
|
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";
|
export const TRAEFIK_VERSION = process.env.TRAEFIK_VERSION || "3.1.2";
|
||||||
|
|
||||||
interface TraefikOptions {
|
interface TraefikOptions {
|
||||||
@@ -23,6 +24,7 @@ interface TraefikOptions {
|
|||||||
publishedPort: number;
|
publishedPort: number;
|
||||||
publishMode?: "ingress" | "host";
|
publishMode?: "ingress" | "host";
|
||||||
}[];
|
}[];
|
||||||
|
force?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const initializeTraefik = async ({
|
export const initializeTraefik = async ({
|
||||||
@@ -30,113 +32,86 @@ export const initializeTraefik = async ({
|
|||||||
env,
|
env,
|
||||||
serverId,
|
serverId,
|
||||||
additionalPorts = [],
|
additionalPorts = [],
|
||||||
|
force = false,
|
||||||
}: TraefikOptions = {}) => {
|
}: TraefikOptions = {}) => {
|
||||||
const { MAIN_TRAEFIK_PATH, DYNAMIC_TRAEFIK_PATH } = paths(!!serverId);
|
const { MAIN_TRAEFIK_PATH, DYNAMIC_TRAEFIK_PATH } = paths(!!serverId);
|
||||||
const imageName = `traefik:v${TRAEFIK_VERSION}`;
|
const imageName = `traefik:v${TRAEFIK_VERSION}`;
|
||||||
const containerName = "dokploy-traefik";
|
const containerName = "dokploy-traefik";
|
||||||
const settings: CreateServiceOptions = {
|
|
||||||
Name: containerName,
|
const exposedPorts: Record<string, {}> = {
|
||||||
TaskTemplate: {
|
[`${TRAEFIK_PORT}/tcp`]: {},
|
||||||
ContainerSpec: {
|
[`${TRAEFIK_SSL_PORT}/tcp`]: {},
|
||||||
Image: imageName,
|
[`${TRAEFIK_HTTP3_PORT}/udp`]: {},
|
||||||
Env: env,
|
|
||||||
Mounts: [
|
|
||||||
{
|
|
||||||
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",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
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
|
|
||||||
? [
|
|
||||||
{
|
|
||||||
TargetPort: 8080,
|
|
||||||
PublishedPort: 8080,
|
|
||||||
PublishMode: "host" as const,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
: []),
|
|
||||||
...additionalPorts.map((port) => ({
|
|
||||||
TargetPort: port.targetPort,
|
|
||||||
PublishedPort: port.publishedPort,
|
|
||||||
PublishMode: port.publishMode || ("host" as const),
|
|
||||||
})),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const portBindings: Record<string, Array<{ HostPort: string }>> = {
|
||||||
|
[`${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) {
|
||||||
|
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,
|
||||||
|
NetworkingConfig: {
|
||||||
|
EndpointsConfig: {
|
||||||
|
"dokploy-network": {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ExposedPorts: exposedPorts,
|
||||||
|
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: portBindings,
|
||||||
|
},
|
||||||
|
Env: env,
|
||||||
|
};
|
||||||
|
|
||||||
const docker = await getRemoteDocker(serverId);
|
const docker = await getRemoteDocker(serverId);
|
||||||
try {
|
try {
|
||||||
if (serverId) {
|
|
||||||
await pullRemoteImage(imageName, serverId);
|
|
||||||
} else {
|
|
||||||
await pullImage(imageName);
|
|
||||||
}
|
|
||||||
|
|
||||||
const service = docker.getService(containerName);
|
|
||||||
const inspect = await service.inspect();
|
|
||||||
|
|
||||||
const existingEnv = inspect.Spec.TaskTemplate.ContainerSpec.Env || [];
|
|
||||||
const updatedEnv = !env ? existingEnv : env;
|
|
||||||
|
|
||||||
const updatedSettings = {
|
|
||||||
...settings,
|
|
||||||
TaskTemplate: {
|
|
||||||
...settings.TaskTemplate,
|
|
||||||
ContainerSpec: {
|
|
||||||
...(settings?.TaskTemplate as ContainerTaskSpec).ContainerSpec,
|
|
||||||
Env: updatedEnv,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
await service.update({
|
|
||||||
version: Number.parseInt(inspect.Version.Index),
|
|
||||||
...updatedSettings,
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log("Traefik Started ✅");
|
|
||||||
} catch (_) {
|
|
||||||
try {
|
try {
|
||||||
await docker.createService(settings);
|
const service = docker.getService("dokploy-traefik");
|
||||||
} catch (error: any) {
|
await service?.remove({ force: true });
|
||||||
if (error?.statusCode !== 409) {
|
await new Promise((resolve) => setTimeout(resolve, 5000));
|
||||||
throw error;
|
} catch (_) {}
|
||||||
|
|
||||||
|
const container = docker.getContainer(containerName);
|
||||||
|
try {
|
||||||
|
const inspect = await container.inspect();
|
||||||
|
if (inspect.State.Status === "running" && !force) {
|
||||||
|
console.log("Traefik already running");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
console.log("Traefik service already exists, continuing...");
|
|
||||||
|
await container.remove({ force: true });
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
}
|
}
|
||||||
console.log("Traefik Not Found: Starting ✅");
|
|
||||||
|
await docker.createContainer(settings);
|
||||||
|
const newContainer = docker.getContainer(containerName);
|
||||||
|
await newContainer.start();
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -212,6 +187,9 @@ export const getDefaultTraefikConfig = () => {
|
|||||||
},
|
},
|
||||||
websecure: {
|
websecure: {
|
||||||
address: `:${TRAEFIK_SSL_PORT}`,
|
address: `:${TRAEFIK_SSL_PORT}`,
|
||||||
|
http3: {
|
||||||
|
advertisedPort: TRAEFIK_HTTP3_PORT,
|
||||||
|
},
|
||||||
...(process.env.NODE_ENV === "production" && {
|
...(process.env.NODE_ENV === "production" && {
|
||||||
http: {
|
http: {
|
||||||
tls: {
|
tls: {
|
||||||
@@ -267,6 +245,9 @@ export const getDefaultServerTraefikConfig = () => {
|
|||||||
},
|
},
|
||||||
websecure: {
|
websecure: {
|
||||||
address: `:${TRAEFIK_SSL_PORT}`,
|
address: `:${TRAEFIK_SSL_PORT}`,
|
||||||
|
http3: {
|
||||||
|
advertisedPort: TRAEFIK_HTTP3_PORT,
|
||||||
|
},
|
||||||
http: {
|
http: {
|
||||||
tls: {
|
tls: {
|
||||||
certResolver: "letsencrypt",
|
certResolver: "letsencrypt",
|
||||||
|
|||||||
Reference in New Issue
Block a user