Skip to content

How to Automate File Backups with Python

Learn how to create automated Python scripts for backing up single files, folders, and compressed archives using shutil, with timestamping and scheduling for Windows, Linux, and macOS.

Data loss is inevitable, but losing work is optional. Automating your backups ensures you never forget to save critical files, whether it's project code, financial reports, or personal documents.

Python's built-in shutil module is the standard tool for high-level file operations. This guide covers backing up single files, entire directories, and compressing them into archives.

Backing Up a Single File

We use shutil.copy2() instead of copy() because it preserves metadata like creation and modification times. Adding a timestamp to the filename ensures we don't overwrite previous backups.

import shutil
import os
from datetime import datetime


def backup_file(source_file: str, backup_dir: str) -> None:
    """
    Create a timestamped backup of a single file.
    """
    os.makedirs(backup_dir, exist_ok=True)

    timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")

    base_name = os.path.basename(source_file)
    target_name = f"{timestamp}_{base_name}"
    target_path = os.path.join(backup_dir, target_name)

    try:
        shutil.copy2(source_file, target_path)
        print(f"✅ Backup successful: {target_path}")
    except FileNotFoundError:
        print(f"❌ Error: Source file '{source_file}' not found.")
    except PermissionError:
        print("❌ Error: Permission denied.")


backup_file("financial_report.xlsx", "backups")

Output:

✅ Backup successful: backups/2026-02-12_17-14-01_financial_report.xlsx

Backing Up an Entire Folder

To clone a directory including all subfolders and files, use shutil.copytree().

import shutil
from datetime import datetime
import os


def backup_folder(source_folder: str, backup_root: str) -> None:
    """
    Create a timestamped backup of an entire directory.
    """
    timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
    folder_name = os.path.basename(source_folder.rstrip('/\\'))
    target_dir = os.path.join(backup_root, f"{folder_name}_{timestamp}")

    try:
        shutil.copytree(source_folder, target_dir)
        print(f"✅ Folder backup created: {target_dir}")
    except FileExistsError:
        print("❌ Error: Backup folder already exists.")
    except FileNotFoundError:
        print(f"❌ Error: Source folder '{source_folder}' not found.")
    except Exception as e:
        print(f"❌ Error: {e}")


backup_folder("my_project", "backups")

Output:

✅ Folder backup created: backups/my_project_2026-01-15_14-30-00

Creating ZIP Archives

Copying raw files consumes significant disk space. Compressing backups into .zip archives is more efficient and easier to manage.

import shutil
from datetime import datetime
import os


def backup_to_zip(source_folder: str, backup_dir: str) -> None:
    """
    Create a compressed ZIP backup of a folder.
    """
    os.makedirs(backup_dir, exist_ok=True)

    timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
    folder_name = os.path.basename(source_folder.rstrip('/\\'))
    archive_name = os.path.join(backup_dir, f"{folder_name}_{timestamp}")

    shutil.make_archive(archive_name, 'zip', source_folder)
    print(f"📦 Archive created: {archive_name}.zip")


backup_to_zip("my_project", "backups")

Output:

📦 Archive created: backups/my_project_2026-01-15_14-30-00.zip

Complete Backup Script

Here's a production-ready script that combines all techniques with configuration and logging:

import shutil
import os
from datetime import datetime
from pathlib import Path


class BackupManager:
    """Manages file and folder backups with compression."""

    def __init__(self, backup_root: str = "backups"):
        self.backup_root = Path(backup_root)
        self.backup_root.mkdir(parents=True, exist_ok=True)

    def _get_timestamp(self) -> str:
        return datetime.now().strftime("%Y-%m-%d_%H-%M-%S")

    def backup_file(self, source: str) -> Path | None:
        """Backup a single file with timestamp."""
        source_path = Path(source)

        if not source_path.exists():
            print(f"❌ Source not found: {source}")
            return None

        target = self.backup_root / f"{self._get_timestamp()}_{source_path.name}"

        try:
            shutil.copy2(source_path, target)
            print(f"✅ File backed up: {target}")
            return target
        except Exception as e:
            print(f"❌ Backup failed: {e}")
            return None

    def backup_folder(self, source: str, compress: bool = True) -> Path | None:
        """Backup a folder, optionally compressed."""
        source_path = Path(source)

        if not source_path.exists():
            print(f"❌ Source not found: {source}")
            return None

        timestamp = self._get_timestamp()
        base_name = f"{source_path.name}_{timestamp}"

        try:
            if compress:
                archive_path = self.backup_root / base_name
                shutil.make_archive(str(archive_path), 'zip', source_path)
                result = archive_path.with_suffix('.zip')
                print(f"📦 Compressed backup: {result}")
            else:
                target = self.backup_root / base_name
                shutil.copytree(source_path, target)
                result = target
                print(f"✅ Folder backed up: {result}")

            return result
        except Exception as e:
            print(f"❌ Backup failed: {e}")
            return None


if __name__ == "__main__":
    manager = BackupManager("daily_backups")

    manager.backup_file("config.yaml")
    manager.backup_file("database.db")

    manager.backup_folder("src", compress=True)
    manager.backup_folder("docs", compress=False)

Scheduling Automatic Backups

A backup script is only useful if it runs automatically.

Windows (Task Scheduler)

  1. Open Task Scheduler
  2. Click Create Basic Task
  3. Set trigger to Daily at your preferred time
  4. Action: Start a Program
    • Program: python.exe (full path)
    • Arguments: C:\path\to\backup_script.py

Linux / macOS (Cron)

Open your crontab:

crontab -e

Add a scheduled job:

0 17 * * * /usr/bin/python3 /home/user/scripts/backup.py >> /var/log/backup.log 2>&1

0 0 * * 0 /usr/bin/python3 /home/user/scripts/backup.py

Function Reference

Function Use Case Preserves Metadata
shutil.copy() Quick file copy No
shutil.copy2() Standard file backup Yes
shutil.copytree() Full directory clone Yes
shutil.make_archive() Compressed archive N/A

Summary

  • Use shutil.copy2() for single files to preserve timestamps.
  • Use shutil.copytree() to clone entire directories.
  • Use shutil.make_archive() for compressed backups that save disk space.
  • Always add timestamps to backup names to maintain version history.
  • Schedule your script with cron or Task Scheduler for true automation.
Navigation