diff --git a/README.md b/README.md
index d192d6f75..e2969f76f 100644
--- a/README.md
+++ b/README.md
@@ -99,6 +99,10 @@ For detailed documentation, visit [docs.dokploy.com](https://docs.dokploy.com).
+
+
+
+
diff --git a/apps/dokploy/package.json b/apps/dokploy/package.json
index ef14fd3b3..86aac4e4e 100644
--- a/apps/dokploy/package.json
+++ b/apps/dokploy/package.json
@@ -1,6 +1,6 @@
{
"name": "dokploy",
- "version": "v0.23.3",
+ "version": "v0.23.4",
"private": true,
"license": "Apache-2.0",
"type": "module",
diff --git a/apps/dokploy/server/api/routers/settings.ts b/apps/dokploy/server/api/routers/settings.ts
index 149af7046..0d07bbea0 100644
--- a/apps/dokploy/server/api/routers/settings.ts
+++ b/apps/dokploy/server/api/routers/settings.ts
@@ -837,6 +837,14 @@ export const settingsRouter = createTRPCRouter({
getLogCleanupStatus: adminProcedure.query(async () => {
return getLogCleanupStatus();
}),
+
+ getDokployCloudIps: adminProcedure.query(async () => {
+ if (!IS_CLOUD) {
+ return [];
+ }
+ const ips = process.env.DOKPLOY_CLOUD_IPS?.split(",");
+ return ips;
+ }),
});
export const getTraefikPorts = async (serverId?: string) => {
diff --git a/packages/server/src/utils/backups/compose.ts b/packages/server/src/utils/backups/compose.ts
index 82fb8828a..2fb808198 100644
--- a/packages/server/src/utils/backups/compose.ts
+++ b/packages/server/src/utils/backups/compose.ts
@@ -15,7 +15,7 @@ export const runComposeBackup = async (
) => {
const { projectId, name } = compose;
const project = await findProjectById(projectId);
- const { prefix } = backup;
+ const { prefix, databaseType } = backup;
const destination = backup.destination;
const backupFileName = `${new Date().toISOString()}.dump.gz`;
const bucketDestination = `${normalizeS3Path(prefix)}${backupFileName}`;
@@ -46,9 +46,10 @@ export const runComposeBackup = async (
await sendDatabaseBackupNotifications({
applicationName: name,
projectName: project.name,
- databaseType: "mongodb",
+ databaseType: getDatabaseType(databaseType),
type: "success",
organizationId: project.organizationId,
+ databaseName: backup.database,
});
await updateDeploymentStatus(deployment.deploymentId, "done");
@@ -57,14 +58,31 @@ export const runComposeBackup = async (
await sendDatabaseBackupNotifications({
applicationName: name,
projectName: project.name,
- databaseType: "mongodb",
+ databaseType: getDatabaseType(databaseType),
type: "error",
// @ts-ignore
errorMessage: error?.message || "Error message not provided",
organizationId: project.organizationId,
+ databaseName: backup.database,
});
await updateDeploymentStatus(deployment.deploymentId, "error");
throw error;
}
};
+
+const getDatabaseType = (databaseType: BackupSchedule["databaseType"]) => {
+ if (databaseType === "mongo") {
+ return "mongodb";
+ }
+ if (databaseType === "postgres") {
+ return "postgres";
+ }
+ if (databaseType === "mariadb") {
+ return "mariadb";
+ }
+ if (databaseType === "mysql") {
+ return "mysql";
+ }
+ return "mongodb";
+};
diff --git a/packages/server/src/utils/backups/mariadb.ts b/packages/server/src/utils/backups/mariadb.ts
index 70bad1698..8760095c8 100644
--- a/packages/server/src/utils/backups/mariadb.ts
+++ b/packages/server/src/utils/backups/mariadb.ts
@@ -48,6 +48,7 @@ export const runMariadbBackup = async (
databaseType: "mariadb",
type: "success",
organizationId: project.organizationId,
+ databaseName: backup.database,
});
await updateDeploymentStatus(deployment.deploymentId, "done");
} catch (error) {
@@ -60,6 +61,7 @@ export const runMariadbBackup = async (
// @ts-ignore
errorMessage: error?.message || "Error message not provided",
organizationId: project.organizationId,
+ databaseName: backup.database,
});
await updateDeploymentStatus(deployment.deploymentId, "error");
throw error;
diff --git a/packages/server/src/utils/backups/mongo.ts b/packages/server/src/utils/backups/mongo.ts
index 806fd5c0f..e626efa01 100644
--- a/packages/server/src/utils/backups/mongo.ts
+++ b/packages/server/src/utils/backups/mongo.ts
@@ -46,6 +46,7 @@ export const runMongoBackup = async (mongo: Mongo, backup: BackupSchedule) => {
databaseType: "mongodb",
type: "success",
organizationId: project.organizationId,
+ databaseName: backup.database,
});
await updateDeploymentStatus(deployment.deploymentId, "done");
} catch (error) {
@@ -58,6 +59,7 @@ export const runMongoBackup = async (mongo: Mongo, backup: BackupSchedule) => {
// @ts-ignore
errorMessage: error?.message || "Error message not provided",
organizationId: project.organizationId,
+ databaseName: backup.database,
});
await updateDeploymentStatus(deployment.deploymentId, "error");
throw error;
diff --git a/packages/server/src/utils/backups/mysql.ts b/packages/server/src/utils/backups/mysql.ts
index 2ba8a0e12..6f6678421 100644
--- a/packages/server/src/utils/backups/mysql.ts
+++ b/packages/server/src/utils/backups/mysql.ts
@@ -47,6 +47,7 @@ export const runMySqlBackup = async (mysql: MySql, backup: BackupSchedule) => {
databaseType: "mysql",
type: "success",
organizationId: project.organizationId,
+ databaseName: backup.database,
});
await updateDeploymentStatus(deployment.deploymentId, "done");
} catch (error) {
@@ -59,6 +60,7 @@ export const runMySqlBackup = async (mysql: MySql, backup: BackupSchedule) => {
// @ts-ignore
errorMessage: error?.message || "Error message not provided",
organizationId: project.organizationId,
+ databaseName: backup.database,
});
await updateDeploymentStatus(deployment.deploymentId, "error");
throw error;
diff --git a/packages/server/src/utils/backups/postgres.ts b/packages/server/src/utils/backups/postgres.ts
index 9ce2025a9..1f7b6a2d2 100644
--- a/packages/server/src/utils/backups/postgres.ts
+++ b/packages/server/src/utils/backups/postgres.ts
@@ -50,6 +50,7 @@ export const runPostgresBackup = async (
databaseType: "postgres",
type: "success",
organizationId: project.organizationId,
+ databaseName: backup.database,
});
await updateDeploymentStatus(deployment.deploymentId, "done");
@@ -62,6 +63,7 @@ export const runPostgresBackup = async (
// @ts-ignore
errorMessage: error?.message || "Error message not provided",
organizationId: project.organizationId,
+ databaseName: backup.database,
});
await updateDeploymentStatus(deployment.deploymentId, "error");
diff --git a/packages/server/src/utils/builders/index.ts b/packages/server/src/utils/builders/index.ts
index 198b953d7..4f8dfa2f3 100644
--- a/packages/server/src/utils/builders/index.ts
+++ b/packages/server/src/utils/builders/index.ts
@@ -1,5 +1,4 @@
import { createWriteStream } from "node:fs";
-import { join } from "node:path";
import type { InferResultType } from "@dokploy/server/types/with";
import type { CreateServiceOptions } from "dockerode";
import { uploadImage, uploadImageRemoteCommand } from "../cluster/upload";
@@ -211,16 +210,20 @@ export const mechanizeDockerContainer = async (
const getImageName = (application: ApplicationNested) => {
const { appName, sourceType, dockerImage, registry } = application;
-
+ const imageName = `${appName}:latest`;
if (sourceType === "docker") {
return dockerImage || "ERROR-NO-IMAGE-PROVIDED";
}
if (registry) {
- return join(registry.registryUrl, registry.imagePrefix || "", appName);
+ const { registryUrl, imagePrefix, username } = registry;
+ const registryTag = imagePrefix
+ ? `${registryUrl}/${imagePrefix}/${imageName}`
+ : `${registryUrl}/${username}/${imageName}`;
+ return registryTag;
}
- return `${appName}:latest`;
+ return imageName;
};
const getAuthConfig = (application: ApplicationNested) => {
diff --git a/packages/server/src/utils/cluster/upload.ts b/packages/server/src/utils/cluster/upload.ts
index 980ace67c..f982d414b 100644
--- a/packages/server/src/utils/cluster/upload.ts
+++ b/packages/server/src/utils/cluster/upload.ts
@@ -1,5 +1,4 @@
import type { WriteStream } from "node:fs";
-import path, { join } from "node:path";
import type { ApplicationNested } from "../builders";
import { spawnAsync } from "../process/spawnAsync";
@@ -13,19 +12,22 @@ export const uploadImage = async (
throw new Error("Registry not found");
}
- const { registryUrl, imagePrefix } = registry;
+ const { registryUrl, imagePrefix, username } = registry;
const { appName } = application;
const imageName = `${appName}:latest`;
const finalURL = registryUrl;
- const registryTag = path
- .join(registryUrl, join(imagePrefix || "", imageName))
- .replace(/\/+/g, "/");
+ // Build registry tag in correct format: registry.com/owner/image:tag
+ // For ghcr.io: ghcr.io/username/image:tag
+ // For docker.io: docker.io/username/image:tag
+ const registryTag = imagePrefix
+ ? `${registryUrl}/${imagePrefix}/${imageName}`
+ : `${registryUrl}/${username}/${imageName}`;
try {
writeStream.write(
- `📦 [Enabled Registry] Uploading image to ${registry.registryType} | ${imageName} | ${finalURL}\n`,
+ `📦 [Enabled Registry] Uploading image to ${registry.registryType} | ${imageName} | ${finalURL} | ${registryTag}\n`,
);
const loginCommand = spawnAsync(
"docker",
@@ -67,15 +69,16 @@ export const uploadImageRemoteCommand = (
throw new Error("Registry not found");
}
- const { registryUrl, imagePrefix } = registry;
+ const { registryUrl, imagePrefix, username } = registry;
const { appName } = application;
const imageName = `${appName}:latest`;
const finalURL = registryUrl;
- const registryTag = path
- .join(registryUrl, join(imagePrefix || "", imageName))
- .replace(/\/+/g, "/");
+ // Build registry tag in correct format: registry.com/owner/image:tag
+ const registryTag = imagePrefix
+ ? `${registryUrl}/${imagePrefix}/${imageName}`
+ : `${registryUrl}/${username}/${imageName}`;
try {
const command = `
diff --git a/packages/server/src/utils/notifications/database-backup.ts b/packages/server/src/utils/notifications/database-backup.ts
index 37a4a1ff2..044e3a0cf 100644
--- a/packages/server/src/utils/notifications/database-backup.ts
+++ b/packages/server/src/utils/notifications/database-backup.ts
@@ -19,6 +19,7 @@ export const sendDatabaseBackupNotifications = async ({
type,
errorMessage,
organizationId,
+ databaseName,
}: {
projectName: string;
applicationName: string;
@@ -26,6 +27,7 @@ export const sendDatabaseBackupNotifications = async ({
type: "error" | "success";
organizationId: string;
errorMessage?: string;
+ databaseName: string;
}) => {
const date = new Date();
const unixDate = ~~(Number(date) / 1000);
@@ -90,6 +92,11 @@ export const sendDatabaseBackupNotifications = async ({
value: databaseType,
inline: true,
},
+ {
+ name: decorate("`📂`", "Database Name"),
+ value: databaseName,
+ inline: true,
+ },
{
name: decorate("`📅`", "Date"),
value: ``,
@@ -136,6 +143,7 @@ export const sendDatabaseBackupNotifications = async ({
`${decorate("🛠️", `Project: ${projectName}`)}` +
`${decorate("⚙️", `Application: ${applicationName}`)}` +
`${decorate("❔", `Type: ${databaseType}`)}` +
+ `${decorate("📂", `Database Name: ${databaseName}`)}` +
`${decorate("🕒", `Date: ${date.toLocaleString()}`)}` +
`${type === "error" && errorMessage ? decorate("❌", `Error:\n${errorMessage}`) : ""}`,
);
@@ -150,7 +158,7 @@ export const sendDatabaseBackupNotifications = async ({
? `\n\nError:\n${errorMessage}`
: "";
- const messageText = `${statusEmoji} Database Backup ${typeStatus}\n\nProject: ${projectName}\nApplication: ${applicationName}\nType: ${databaseType}\nDate: ${format(date, "PP")}\nTime: ${format(date, "pp")}${isError ? errorMsg : ""}`;
+ const messageText = `${statusEmoji} Database Backup ${typeStatus}\n\nProject: ${projectName}\nApplication: ${applicationName}\nType: ${databaseType}\nDatabase Name: ${databaseName}\nDate: ${format(date, "PP")}\nTime: ${format(date, "pp")}${isError ? errorMsg : ""}`;
await sendTelegramNotification(telegram, messageText);
}
@@ -191,6 +199,10 @@ export const sendDatabaseBackupNotifications = async ({
value: databaseType,
short: true,
},
+ {
+ title: "Database Name",
+ value: databaseName,
+ },
{
title: "Time",
value: date.toLocaleString(),