Automatically Transferring DB Dumps, Backup, and Archive Files to Remote Storage

IT EngineersDevelopers

Getting Started

Core Concept

Automatically transfer database dumps, backups, and archive files to designated locations

Database and backup systems generate various files, including DB dumps, full backup files, incremental backup files, and log archives.

Automated DB and backup file transfer checks for generated files and automatically transfers them to a remote data center or cloud storage according to configured conditions.

By connecting the location where files are generated with the remote storage environment, regularly generated files can be managed according to a defined flow.

text
DB / Backup System
       │
       ▼
  File Creation Check
       │
       ▼
  Transfer Condition Check
       │
       ▼
 Remote Storage Environment
       │
       ├── Remote Data Center
       │
       └── Cloud Storage

Automation Flow

Connect the process from backup file creation through remote storage and result verification in sequence

When a backup file is created or a backup operation is completed, the next transfer operation can be run according to configured conditions.

You can also configure a flow that checks files generated on a defined schedule and transfers them to the remote storage environment.

① Create a DB dump or backup file

② Confirm file or backup completion

③ Run the transfer operation

④ Store in the remote environment

⑤ Review the result

This flow automatically connects backup file creation and remote storage operations in sequence.

Operational Benefits

Manage regular backup file transfers and remote storage as a single flow

DB and backup environments continuously manage file creation, remote transfers, storage locations, and execution results.

By configuring an automated transfer flow, regularly generated files can be transferred to designated remote environments while execution results and storage status are reviewed together.

CategoryIndividual ManagementAutomated Transfer
File CheckCheck generated files for each operationCheck according to configured conditions
Transfer ExecutionRun each file transfer manuallyRun automatically according to conditions and schedules
Storage LocationSpecify the destination for each operationConfigure paths by file type
Result ManagementReview results by operationReview execution history and storage results together

This lets you build a single operational flow from DB dump and backup file creation through remote storage.

IT Engineer

Source Connection

Connect the file creation locations of database and backup equipment

First, connect the devices and folders where DB dumps and backup files are created to the transfer environment.

Specify file paths generated on database servers, backup servers, or storage systems and configure them as transfer sources.

Source EnvironmentGenerated Files
Database ServerDB dumps and export files
Backup ServerFull and incremental backup files
Log ServerTransaction logs and archives
StorageFiles for long-term retention

Transfer Configuration

Configure the remote storage location and execution conditions as a single transfer flow

After connecting the source files, configure the remote data center or cloud storage as the transfer target.

You can configure the transfer to start when a file is created, when a backup operation is completed, or according to a defined schedule.

Configuration ItemConfiguration
Remote TargetData center or cloud storage
Storage LocationFolder or bucket path by file type
Execution ConditionFile creation, backup completion, scheduled execution
File TypeDB Dump, Backup, Archive, etc.

For example, you can configure a flow that sends daily backups to cloud storage while sending weekly backups and long-term retention files to separate remote paths.

text
Backup Operation
    │
    ▼
Backup Complete
    │
    ▼
File Check
    │
    ├── DB Dump ────────────→ Remote Data Center
    │
    ├── Daily Backup ───────→ Cloud Storage
    │
    └── Archive ────────────→ Long-Term Retention Path

Automated Transfer

Automatically transfer generated backup files to the designated remote environment

When the configured conditions are met, DB dumps and backup files are transferred to the designated remote environment.

You can branch to different storage locations by file type and path, or configure a single backup file to be transferred to multiple remote environments.

text
                Backup File
                    │
                    ▼
                Transfer Operation
              ╱           ╲
             ▼             ▼
      Remote Data Center   Cloud Storage
             │             │
             ▼             ▼
        Backup Storage      Bucket / Path

Result Management

Review transfer status and storage results, then rerun required operations

When a transfer operation runs, you can review file processing status and remote storage results through Runs and the operation details.

If a specific operation requires additional review, check the source file, device connection, target path, and access scope through the Activity Log and execution information, then rerun the required operation.

text
Transfer Operation
    │
    ▼
Operation Status Check
    │
    ├── Running
    │      │
    │      └── Review Progress
    │
    ├── Completed
    │      │
    │      └── Review Storage Result
    │
    └── Review Required
           │
           ▼
       Detailed Record Review
           │
           ▼
 Original, Connection, and Storage Path Check
           │
           ▼
       Rerun Required Operation
           │
           ▼
        Review Result Again
Review ItemDetails
Source FileTransferred DB dumps and backup files
Target LocationRemote server or cloud storage path
Progress StatusCurrent operation status and progress
Processing ResultNumber of transferred files and total size
Execution HistoryFile processing steps and operation results

Developer

Transfer the dump remotely when the backup completes and verify it with a checksum

Integration Preparation

Prepare common request code and path representation

import os
import requests

BASE_URL = os.getenv("INNORIX_BASE_URL", "https://app.innorix.com").rstrip("/")
TOKEN = os.environ["INNORIX_ACCESS_TOKEN"]
WORKSPACE_ID = os.getenv("INNORIX_WORKSPACE_ID")   # optional; falls back to the current workspace

STATUS_COMPLETE = 2
TERMINAL = {2, 4, 5, 9, 99}          # complete / error / cancelled / partial / failed
NOT_SUCCEEDED = {4, 5, 9, 99}


def api(method, path, body=None, params=None):
    headers = {
        "Content-Type": "application/json",
        "Authorization": f"Bearer {TOKEN}",
    }

    if WORKSPACE_ID:
        headers["x-workspace-id"] = WORKSPACE_ID

    response = requests.request(
        method, BASE_URL + path,
        headers=headers, json=body, params=params, timeout=30,
    )

    payload = response.json() if response.content else {}

    if not response.ok:
        raise RuntimeError(payload.get("message") or f"HTTP {response.status_code}")

    return payload.get("data")


def is_terminal(detail):
    return detail.get("isTerminal", detail.get("status") in TERMINAL)
import base64
import time


def encode_path(device_id, raw_path):
    normalized = str(raw_path or "").replace("\\", "/")
    token = base64.b64encode(normalized.encode("utf-8")).decode("ascii")
    return f"{device_id}_ino_{token}"


def now_iso():
    return time.strftime("%Y-%m-%dT%H:%M:%S.000Z", time.gmtime())

Determine the transfer status using the values below. There are five terminal states, and the successful value is Complete (2).

Status ValueMeaningTerminal
2CompleteYes
4ErrorYes
5CancelledYes
9Partially CompleteYes
99FailedYes
1 , 6 , 12 , 13Started, Transferring, Synchronizing, ReceivingNo

Transfer After Backup Completion

Continue the transfer from the end of the backup script

Calling the transfer immediately after the backup program creates the file ensures that the file is ready at the correct point in time.

When sending a file, explicitly set isDir: false in sourceItem. sourcePaths treats every path as a folder, so passing a dump file causes the server to scan it as a folder, which can slow the operation or cause a timeout.

import glob
import os


def latest_backup(directory, pattern="*.dump"):
    files = glob.glob(os.path.join(directory, pattern))

    if not files:
        raise FileNotFoundError(directory)

    return max(files, key=os.path.getmtime)


def send_backup(source, target, backup_path, target_path):
    transfer = api("POST", "/api/transfers/manual", {
        "sourceDevice": source,
        "targetDevice": target,
        "targetPath": target_path,
        "sourceItem": [{
            "path": backup_path,
            "isDir": False,
            "fileSize": os.path.getsize(backup_path),
        }],
        "sendAllFolder": False,
        "checkIntegrity": True,
        "transferOptions": {"target-action": "numbering"},
    })

    return transfer["monitorId"]

Because backup files must retain each backup iteration, set the destination policy to numbering. If you use overwrite, the previous backup is removed and you cannot select the recovery point.

Passing the file size along lets the server skip querying the size of each item again, improving performance.

A corrupted backup may only be discovered when recovery is attempted, so use checkIntegrity to verify integrity during the transfer stage.

Branch by File Type

Send dumps, incrementals, and archives to different locations

A single transfer handles one target. If targets differ by file type, create separate transfers.

ROUTES = {
    "dump":    ("device-dc-01", "/backup/dump"),
    "daily":   ("device-cloud-01", "/backup/daily"),
    "archive": ("device-archive-01", "/backup/archive"),
}


def classify(filename):
    name = os.path.basename(filename).lower()

    if name.endswith(".dump"):
        return "dump"

    if "archive" in name or name.endswith(".tar.gz"):
        return "archive"

    return "daily"


def dispatch(source, files):
    transfers = {}

    for path in files:
        target, target_path = ROUTES[classify(path)]
        transfers[path] = send_backup(source, target, path, target_path)

    return transfers

If a single backup file must be sent to multiple remote environments, create a transfer for each target and store the returned monitorId for each target so you can query each transfer later.

copies = {
    target: send_backup("device-db-01", target, backup_path, path)
    for target, path in [("device-dc-01", "/backup/dump"),
                         ("device-cloud-01", "/backup/mirror")]
}

Schedule Automation

Register the operation to run repeatedly at a defined time

If the backup script cannot be modified, configure it through schedule automation.

def build_schedule_automation(name, source, source_path, target, target_path,
                              schedule):
    return {
        "name": name,
        "flowName": name,
        "transferType": "normal",
        "timezone": "Asia/Seoul",
        "step": 1,
        "isUpcoming": False,
        "details": [
            {
                "senderId": source,
                "receiverId": target,
                "sourceItem": [
                    {
                        "hash": encode_path(source, source_path),
                        "filePath": source_path,
                        "isDir": True,
                    }
                ],
                "targetPath": encode_path(target, target_path),
                "step": 1,
                "transferOptions": {
                    "noSchedule": False,
                    "target-action": "numbering",
                    "send-fileoption": {},
                },
            }
        ],
        "schedules": [schedule],
    }


DAILY_3AM = {
    "type": "day",
    "startDateType": "now",
    "hour": "03",
    "minute": "00",
    "ampm": "am",
    "startDate": now_iso(),
    "timezone": "Asia/Seoul",
}

MONTHLY = {
    "type": "month",
    "startDateType": "now",
    "day": "1",
    "hour": "04",
    "minute": "00",
    "ampm": "am",
    "startDate": now_iso(),
    "timezone": "Asia/Seoul",
}

api("POST", "/api/automations", build_schedule_automation(
    "daily backup", "device-db-01", "/backup",
    "device-dc-01", "/backup/daily", DAILY_3AM))

There are four items that must be followed in an automation request.

ItemSpecification
isUpcomingMust be false. The server default true ignores the schedule in the request and replaces it with a five-minute one-time schedule. Steps with triggerAutomation are forced to false by the server, so specify it directly only on the first step without a trigger.
stepInclude it at both the top level and in details. It represents the hop position in the flow.
sourceItemInclude both hash (path token) and filePath (plain-text path).
syncTypePut it inside transferOptions. 1 is one-way and 2 is bidirectional.

All four items can be omitted and registration will still succeed, but behavior changes at execution time. If a recurring schedule was registered but runs only once and stops, check isUpcoming first.

Preventing Duplicate Registration

Prevent the same backup operation from being created twice

A new automation is created even when an automation with the same name already exists. If a batch is retried, the same backup is transferred twice, and because the destination policy is numbering, two copies of the file accumulate.

def find_automation(name):
    # the name we send is stored as flowName in the response
    # automationName is a server generated id like T4037-8500-1815, not the name we set.
    for page in range(1, 6):
        result = api("GET", "/api/automations",
                     params={"page": page, "size": 100, "search": name}) or {}

        items = [item
                 for flow in result.get("automations") or []
                 for item in flow.get("automations") or []]

        for item in items:
            if item.get("flowName") == name:
                return item

        if len(items) < 100:
            return None

    return None

The automation list is returned nested by flow group, so you must iterate through the inner arrays as well. Server search uses partial matching, so select only the item whose name exactly matches the name received.

Integrity Verification

Verify that the backup stored remotely is identical to the original

def verify(monitor_id, timeout=1800, interval=10):
    api("POST", f"/api/transfers/{monitor_id}/verification", {})

    deadline = time.time() + timeout

    while time.time() < deadline:
        result = api("GET", f"/api/transfers/{monitor_id}/verification") or {}

        if result.get("verified"):
            return result

        time.sleep(interval)

    raise TimeoutError(monitor_id)
result = verify(monitor_id)

if not result["checksumMatched"]:
    alert(f"backup integrity failed: {result.get('mismatchedFiles')}")
Response ItemDetails
checksumAlgorithmChecksum algorithm used
sourceFileCount , targetFileCountFile counts at the source and remote destination
checksumMatchedWhether the checksums match
mismatchedCountNumber of mismatches

If the file counts differ, the transfer is incomplete. If the counts match but there are mismatches, the contents are corrupted. The latter is more dangerous for backups, so report verification failure immediately.

Review Results and Retransmit

Review transfer results and retransmit failed files

def wait(monitor_id, timeout=3600, interval=3):
    deadline = time.time() + timeout

    while time.time() < deadline:
        detail = api("GET", f"/api/transfers/{monitor_id}")

        if is_terminal(detail):
            return detail

        time.sleep(interval)

    raise TimeoutError(monitor_id)


def failed_files(monitor_id):
    result = api("GET", f"/api/transfers/{monitor_id}/files", params={
        "state": "any", "size": 500,
    }) or {}

    return [r for r in (result.get("children") or [])
            if r.get("status") in NOT_SUCCEEDED]


def retry_failed(monitor_id):
    rows = failed_files(monitor_id)

    if not rows:
        return 0

    api("POST", f"/api/transfers/{monitor_id}/retry", {
        "filesRetry": [
            {"filePath": r["sourceFilePath"], "isDir": bool(r.get("isFolder"))}
            for r in rows
        ]
    })

    return len(rows)
detail = wait(monitor_id, timeout=7200)

if detail["status"] != STATUS_COMPLETE:
    alert(f"backup transfer failed - retried {retry_failed(monitor_id)} files")

To check whether the immediately preceding run of a daily operation ended normally, query recent history over a defined period.

from datetime import datetime, timedelta, timezone


def paginate(path, params=None, limit=200, max_pages=50):
    query = dict(params or {})
    query["limit"] = limit
    cursor = None

    for _ in range(max_pages):
        if cursor:
            query["cursor"] = cursor

        result = api("GET", path, params=query) or {}

        for record in result.get("data") or []:
            yield record

        pagination = result.get("pagination") or {}

        if not pagination.get("hasMore"):
            return

        cursor = pagination.get("nextCursor")

        if not cursor:
            return


end = datetime.now(timezone.utc)
fmt = "%Y-%m-%dT%H:%M:%SZ"

rows = list(paginate("/api/transfer-history", params={
    "startDate": (end - timedelta(days=1)).strftime(fmt),
    "endDate": end.strftime(fmt),
}))

failures = [r for r in rows if r.get("status") in NOT_SUCCEEDED]

if failures:
    alert(f"{len(failures)} backup transfers failed yesterday")

If you need the complete history as a file for auditing, use CSV export.

params = {
    "periodDays": 30,
    "page": 1,
    "size": 10000,
    "filter": "[]",              # the server parses this as a JSON string, so send an empty array
    "sort": "startDate:desc",
}

Even when there are no conditions, send an empty array string ("[]") in filter. The server parses this value as JSON. Fields that can be used in sort are status, sourceDeviceName, targetDeviceName, totalSize, sourceFileCount, startDate, endDate, automationName, formattedTransferTime, and savedTime.

Review ItemDetails
SourceTransferred dumps and backup files
TargetRemote data center or cloud path
VerificationFile count and checksum match
StatusTransfer status and success
HistoryExecution result of the most recent run
RetransmissionFailed files and processing results