refactor: enhance backup functionality by incorporating appName and serviceName for S3 bucket paths

This commit is contained in:
Mauricio Siu
2026-03-07 23:32:41 -06:00
parent dc8ff78ee5
commit 922b4d58f1
8 changed files with 51 additions and 15 deletions

View File

@@ -14,13 +14,14 @@ export const runComposeBackup = async (
compose: Compose,
backup: BackupSchedule,
) => {
const { environmentId, name } = compose;
const { environmentId, name, appName } = compose;
const environment = await findEnvironmentById(environmentId);
const project = await findProjectById(environment.projectId);
const { prefix, databaseType } = backup;
const { prefix, databaseType, serviceName } = backup;
const destination = backup.destination;
const backupFileName = `${new Date().toISOString()}.sql.gz`;
const bucketDestination = `${backup.appName}/${normalizeS3Path(prefix)}${backupFileName}`;
const s3AppName = serviceName ? `${appName}_${serviceName}` : appName;
const bucketDestination = `${s3AppName}/${normalizeS3Path(prefix)}${backupFileName}`;
const deployment = await createDeploymentBackup({
backupId: backup.backupId,
title: "Compose Backup",

View File

@@ -106,6 +106,20 @@ export const initCronJobs = async () => {
}
};
const getServiceAppName = (backup: BackupSchedule): string => {
if (backup.compose?.appName) {
return backup.serviceName
? `${backup.compose.appName}_${backup.serviceName}`
: backup.compose.appName;
}
const serviceAppName =
backup.postgres?.appName ||
backup.mysql?.appName ||
backup.mariadb?.appName ||
backup.mongo?.appName;
return serviceAppName || backup.appName;
};
export const keepLatestNBackups = async (
backup: BackupSchedule,
serverId?: string | null,
@@ -116,7 +130,8 @@ export const keepLatestNBackups = async (
try {
const rcloneFlags = getS3Credentials(backup.destination);
const backupFilesPath = `:s3:${backup.destination.bucket}/${backup.appName}/${normalizeS3Path(backup.prefix)}`;
const appName = getServiceAppName(backup);
const backupFilesPath = `:s3:${backup.destination.bucket}/${appName}/${normalizeS3Path(backup.prefix)}`;
// --include "*.sql.gz" or "*.zip" ensures nothing else other than the dokploy backup files are touched by rclone
const rcloneList = `rclone lsf ${rcloneFlags.join(" ")} --include "*${backup.databaseType === "web-server" ? ".zip" : ".sql.gz"}" ${backupFilesPath}`;

View File

@@ -14,13 +14,13 @@ export const runMariadbBackup = async (
mariadb: Mariadb,
backup: BackupSchedule,
) => {
const { environmentId, name } = mariadb;
const { environmentId, name, appName } = mariadb;
const environment = await findEnvironmentById(environmentId);
const project = await findProjectById(environment.projectId);
const { prefix } = backup;
const destination = backup.destination;
const backupFileName = `${new Date().toISOString()}.sql.gz`;
const bucketDestination = `${backup.appName}/${normalizeS3Path(prefix)}${backupFileName}`;
const bucketDestination = `${appName}/${normalizeS3Path(prefix)}${backupFileName}`;
const deployment = await createDeploymentBackup({
backupId: backup.backupId,
title: "MariaDB Backup",

View File

@@ -11,13 +11,13 @@ import { execAsync, execAsyncRemote } from "../process/execAsync";
import { getBackupCommand, getS3Credentials, normalizeS3Path } from "./utils";
export const runMongoBackup = async (mongo: Mongo, backup: BackupSchedule) => {
const { environmentId, name } = mongo;
const { environmentId, name, appName } = mongo;
const environment = await findEnvironmentById(environmentId);
const project = await findProjectById(environment.projectId);
const { prefix } = backup;
const destination = backup.destination;
const backupFileName = `${new Date().toISOString()}.sql.gz`;
const bucketDestination = `${backup.appName}/${normalizeS3Path(prefix)}${backupFileName}`;
const bucketDestination = `${appName}/${normalizeS3Path(prefix)}${backupFileName}`;
const deployment = await createDeploymentBackup({
backupId: backup.backupId,
title: "MongoDB Backup",

View File

@@ -11,13 +11,13 @@ import { execAsync, execAsyncRemote } from "../process/execAsync";
import { getBackupCommand, getS3Credentials, normalizeS3Path } from "./utils";
export const runMySqlBackup = async (mysql: MySql, backup: BackupSchedule) => {
const { environmentId, name } = mysql;
const { environmentId, name, appName } = mysql;
const environment = await findEnvironmentById(environmentId);
const project = await findProjectById(environment.projectId);
const { prefix } = backup;
const destination = backup.destination;
const backupFileName = `${new Date().toISOString()}.sql.gz`;
const bucketDestination = `${backup.appName}/${normalizeS3Path(prefix)}${backupFileName}`;
const bucketDestination = `${appName}/${normalizeS3Path(prefix)}${backupFileName}`;
const deployment = await createDeploymentBackup({
backupId: backup.backupId,
title: "MySQL Backup",

View File

@@ -14,7 +14,7 @@ export const runPostgresBackup = async (
postgres: Postgres,
backup: BackupSchedule,
) => {
const { name, environmentId } = postgres;
const { name, environmentId, appName } = postgres;
const environment = await findEnvironmentById(environmentId);
const project = await findProjectById(environment.projectId);
@@ -26,7 +26,7 @@ export const runPostgresBackup = async (
const { prefix } = backup;
const destination = backup.destination;
const backupFileName = `${new Date().toISOString()}.sql.gz`;
const bucketDestination = `${backup.appName}/${normalizeS3Path(prefix)}${backupFileName}`;
const bucketDestination = `${appName}/${normalizeS3Path(prefix)}${backupFileName}`;
try {
const rcloneFlags = getS3Credentials(destination);
const rcloneDestination = `:s3:${destination.bucket}/${bucketDestination}`;

View File

@@ -4,6 +4,24 @@ import { findComposeById } from "@dokploy/server/services/compose";
import type { findVolumeBackupById } from "@dokploy/server/services/volume-backups";
import { getS3Credentials, normalizeS3Path } from "../backups/utils";
export const getVolumeServiceAppName = (
volumeBackup: Awaited<ReturnType<typeof findVolumeBackupById>>,
): string => {
if (volumeBackup.compose?.appName) {
return volumeBackup.serviceName
? `${volumeBackup.compose.appName}_${volumeBackup.serviceName}`
: volumeBackup.compose.appName;
}
const serviceAppName =
volumeBackup.application?.appName ||
volumeBackup.postgres?.appName ||
volumeBackup.mysql?.appName ||
volumeBackup.mariadb?.appName ||
volumeBackup.mongo?.appName ||
volumeBackup.redis?.appName;
return serviceAppName || volumeBackup.appName;
};
export const backupVolume = async (
volumeBackup: Awaited<ReturnType<typeof findVolumeBackupById>>,
) => {
@@ -12,8 +30,9 @@ export const backupVolume = async (
volumeBackup.application?.serverId || volumeBackup.compose?.serverId;
const { VOLUME_BACKUPS_PATH, VOLUME_BACKUP_LOCK_PATH } = paths(!!serverId);
const destination = volumeBackup.destination;
const s3AppName = getVolumeServiceAppName(volumeBackup);
const backupFileName = `${volumeName}-${new Date().toISOString()}.tar`;
const bucketDestination = `${volumeBackup.appName}/${normalizeS3Path(prefix || "")}${backupFileName}`;
const bucketDestination = `${s3AppName}/${normalizeS3Path(prefix || "")}${backupFileName}`;
const rcloneFlags = getS3Credentials(volumeBackup.destination);
const rcloneDestination = `:s3:${destination.bucket}/${bucketDestination}`;
const volumeBackupPath = path.join(VOLUME_BACKUPS_PATH, volumeBackup.appName);

View File

@@ -12,7 +12,7 @@ import {
import { scheduledJobs, scheduleJob } from "node-schedule";
import { getS3Credentials, normalizeS3Path } from "../backups/utils";
import { sendVolumeBackupNotifications } from "../notifications/volume-backup";
import { backupVolume } from "./backup";
import { backupVolume, getVolumeServiceAppName } from "./backup";
// Helper functions to extract project info from volume backup
const getProjectName = (
@@ -81,7 +81,8 @@ const cleanupOldVolumeBackups = async (
try {
const rcloneFlags = getS3Credentials(destination);
const backupFilesPath = `:s3:${destination.bucket}/${volumeBackup.appName}/${normalizeS3Path(prefix || "")}`;
const s3AppName = getVolumeServiceAppName(volumeBackup);
const backupFilesPath = `:s3:${destination.bucket}/${s3AppName}/${normalizeS3Path(prefix || "")}`;
const listCommand = `rclone lsf ${rcloneFlags.join(" ")} --include \"${volumeName}-*.tar\" ${backupFilesPath}`;
const sortAndPick = `sort -r | tail -n +$((${keepLatestCount}+1)) | xargs -I{}`;
const deleteCommand = `rclone delete ${rcloneFlags.join(" ")} ${backupFilesPath}{}`;