Linux backup time machine based on Python + Rsync + Mysql

in a corporate network chat:
xxx: I was hoping, that I woudln’t have to say it, but the circumstaces made me.
xxx: gentelmen
xxx: did somebody made a backup?

Yet another backup system for Linux based operation systems. Built on Python, using Rsync. Other backup solutions appeared to be too complicated (like bacula) or not having needed functions (rdiff-backup).

Pros of Linux Time MAchine

  • Works on the principle of MacOs’ TimeMachine, namely: creating incremental copies in separated folders with the ability to fastly recovery any file from any date, or use it in some another way
  • Incremental copies, based on HardLinks, so every folder contains sort of “full copy”, but non-changed files represented by hardlinks to their older versions for previous dates
  • Easy thinning of backup copies, because of hardlinks make any copy self-sufficient
  • Immediate access to files, thanks to files stored in a plain form (unlike rdiff-backup and some others)
  • Built-in functionality to create SQL-dumps of MySQL databases (to backup mysql-tables as files)
  • Automatically resume of interruped backup, from the place, where it were stopped, in case of fault
  • Backup process could be started from PC, where backups are stored, or from PC where source files are.
  • Backup frequency could be configures, so, you can limit maximal frequence, to make Linux Time Machine copy files not too often, store this information in the config file, and not move it to crontab (as it usual happen with sheduled operations like backup)
  • Easy to improve backup process by using the API, for example, add copying to several storage servers
  • Copy whole filesystem or just single folder
  • Posibility to exclude folders, files by names or masks (like *.log)
  • Function to automatically clean old, outdated copies, configured by flexible rules
  • Posibility to configure sending error message to the getSentry system (Error-logging monitor with handy web-interface)


  • The data is stored in a clean form, without compression, so, backup could use several times more space, that source data
  • Rsync should be install on both sides (source and destination) for backup to be possible
  • Another requirement is an ssh access to both systems
  • Ssh access is currently working only with key-based authentication
  • When calculating space, consumed by backup copies, single file could be counted several times (because of hardlinks), so space, occupied by it would be measured wrong (but, for example, terminal programm “du” does it the right way)

This tool based on ideas, described in an article:

Work principles:

  • After programm is started, it reads configuration of so-called “variants” of backup copy. Every “variant” describe what to copy, where to store it, and some options about backup.
  • Backup for every configured “variant” is started sequentally
  • If it needed, mysqldump is launched, storing specified database tables in form of sql-files to the specified place. If some sql-files already exists where, they are checked for the needness of update, and, updated only if needed. So, only changed tables are dumped.
  • There are new folder created in the descination folder and named like “in-progress-2015-05-11_04:55:10” (or, using old one with such name for continue previous interrupted backup)
  • Using rsync, source folder copied into “in-progress-…” folder, but not fully. Files, that are not changed till previous backup, hardlinked, to the older versions. In result we have complete copy of source folder.
  • After copy is finished, prefix “in-progress-” is removed from folder name, and symlink “Latest” is created to it. This symlink represents “the most recent copy”, and used by rsync to hardlink non-changed files to.
  • If needed, mysqldumps removed from source folder


Right now, as project is in active development stage, installation is possible from github. Later I’ll put in on PyPi

I’ll describe the installation steps for debian-like linux distribution:

0. Install git, if you haven’t done it later

# apt-get install git

1. Clone repo:

$ git clone

2. Install rsync:

# apt-get install rsync

3. Install Python, pip package maanger, and Python’s packages, needed for script to work:

# apt-get install python3 python3-pip
# pip3 install pexpect pyyaml click

4. Install rsync on all the machines, that should be backuped, and that are the storages of backup copies.

On remote PC run:

apt-get install rsync

5. Setup ssh authorizations by key on all machines, that should be backuped, and storages of backup copies

On local PC:

cat ~/.ssh/

If nothing would be printed, you have to generate key:


And repeat “cat ~/.ssh/”.
Result would be like “ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDLuC89TPgs4pX1TjR……………………1bUnt/XKx3tFvDqMag0xQpdVCHwWcj1/Q+m//56w9DYTOGXXGBysnNNwIk4 Username@PC”.
Copy the result of “cat …” to clipboard.

On remote PC run (paste copied line to quotes instead of example one):

echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDLuC89TPgs4pX1TjR........................1bUnt/XKx3tFvDqMag0xQpdVCHwWcj1/Q+m//56w9DYTOGXXGBysnNNwIk4 Username@PC" > ~/.ssh/authorized_keys


The simplies configuration file can look like:

# variable "variants" should be specified in config file, written on python
variants = {
    # the name of "variant" of backup
    "user_dir": {
        # Source of backup (path and ssh-host)
        'src': {'path':'/home/user, 'host':''},
        # Where to copy (path and ssh-host)
        'dest': {'path':'/home/backuper/user_backup', 'host':'backuper@myserver.local'}

Configuration of backup copy variants should be a file in a JSON, YAML or PYTHON format. This file should contain a dictionary with set of “variants” of backup.
Every variant defines, what should be copied, where to copy it, what files need to be excluded from copy, does it needed to make Mysqk dump before the backup itself starts, and, if needed, what tables and databases need to be backuped, and what doesn’t.

Config files should have extesnions *.py, *.json, *.yml or *.yaml, so the script can determine the type of each one, and read it properly.

I’ll show an examples of config files in Python format,, beacuse I can put comments in Python file (unline json and yaml, where it’s not possible).
Json and yaml files should have similar format.

Example config file, where all possible options are set:

variants = {
    # This would be backup copy of user's home folder
    "laptop_home_user" : {
        # Source of backup copy
        "src": {
            "path": "/home/user",  # Source folder
            "host": "user@laptop"  # SSH host, in form user@host, used to access remote host via SSH
        # Where to copy (analogous to "src")
            "path": "/home/backuper/backup/machines/my_laptop/user", 
            "host": "" # Empty host means, that we want to use current local PC
        # Exceptions:
            # exclude files by mask
            "*.bak", "*~", "*.log", "*.pyc",    
            # exclude concrete folders
            "user/Downloads", "user/Torrents",  
            # exclude files by mask in longer path
            # exclude concrete file
        # Mysql dump configuration
        "mysqldump": { 
            # User and password of database
            "user": "mysqluser", "password", "mysqlpassword",  
            # SSH-host, where mysqldump should be executed (usually the same as 'host' of 'src')
            "host": "user@laptop",  
            # Filters for databases and tables, that should be dumped (rules applied sequentally).
            "dbs_filter": [
                # Add all databases and tables ("*" is a special sign, means ".*" regular expression)
                ["include", "*", "*"],   
                # Exclude all databases, that ends with "_recovery" (by using regular expression)
                ["exclude", ".*_recovery", ""],  
                # Exclude all tables, that contains "logs" in their names, or ends with "_logs" from all databases
                ["exclude", "*", ["logs", ".*_logs"]], 
                # Now, add table "logs" for database "mydb"
                ["include", "mydb", "logs"], 
                # And exclude one BIG table (it would be dumped by another "variant")
                ["exclude", "sentry", "sentry_message"]
            # Folder on remote host, where to place dumped files,
            # it should be inside of folder ['src']['path']
            # so, dumped files would be copied with other files 
            # (mysqldump is runned before files backup)
            "folder": "/home/user/mysqldump",
            # Make dump for tables, that are not changed till last backup
            # (before dump started, tables are checked for changes till last time)
            "force_dump": False  
        # Minimal interval between copies
        "min_timedelta": "1 hours 30 minutes",
        # Options to configure cleaning of old copies of backup
        "sweep": {
            # For last day, we are storing all copies
            "last day": "all",
            # For last week, we are storing 2 copies per day
            "last week": "2 per day",
            # For last month, we are storing 1 copy per day
            "last month": "1 per day",
            # For last 3 months, we are storing 1 copy per 2 days
            "last 3 months": "1 per 2 days",
            # For last 6 months, we are storing 1 copy per week
            "last 6 months": "1 per week",
            # For last year, we are storing 1 copy per month
            "last year":"1 per month"

    # This is a backup of a single big mysql database table, that was excluded in previous backup config:
    "laptop_sentry_message": {
        # From where to copy
        "src": {      
            "path": "/home/user",  # Source folder
            "host": "user@laptop"  # Source SSH host (in form 'user@host')
        # where to copy backup?
            "path": "/home/backuper/backup/machines/my_laptop/user", 
            "host": ""
        # Exceptions (now are empty)
        # Mysql dump config
        "mysqldump": { 
            # Database user and password
            "user": "mysqluser", "password", "mysqlpassword",  
            # SSH-host, where mysql server is located
            "host": "user@laptop",  
            # Filters of mysql and databases, that should be dumped
            "filters": [
                # Add big table, that was excluded at brevious backup
                ["include", "sentry", "sentry_message"]
            # Remote host folder
            "folder": "/home/user/mysqldump_big",
            # Remove dump after backup is done?
            "remove_after_backup": True
        # Minimal interval between backups (2 days and 12 hours = 2.5 days)
        "min_timedelta": {"days":2, "hours":12},
        # Configuration of cleaning old backup copies
        "sweep": {
            # For last day store all copies
            "last day": "all",
            # For last month store one copy per day
            "last month": "1 per day",
            # For last year store one copy per month
            "last year":"1 per month",
            # For last 10 years store two copies per month
            "last 10 years": "2 per year"

By default, “variants” configuration files searched in ~/.config/LinuxTimeMachine/variants, but you can specify config dir, of separate config files in command line.

“Main configuration” file

For global parameters, applied to all “variants” there are special config file, called “main configuration”, that usually looked for on path ~/.config/LinuxTimeMachine/mainconf.json, which could be overriden by command line parameter “–mainconf”.
Main configuration file could have “*.yaml” and “*.py” extension, and it will be reed accordingly (if main configuration is placed in a *.py file, this file should contain “conf” variable at a module level, containing dict, or something similar).

File example:

conf = {
    # DSN address for connection to getSentry (is needed)
    # default sweep configuration (could be used in backup variants, if needed)
        "sweep": {
            # For last day store all backup copies
            "last day": "all",
            # For last week store two copies per day
            "last week": "2 per day",
            # For last month store 1 copy per day
            "last month": "1 per day",
            # For last 3 months store 1 copy per every 2 days
            "last 3 months": "1 per 2 days",
            # For last six months store only 1 copy per week
            "last 6 months": "1 per week",
            # For the last year store 1 copy per month
            "last year":"1 per month"
            # For all further times store 2 copies per year
            "all other":"2 per year"


Для запуска достаточно выполнить скрипт, указав ему команду backup, он прочитает варианты из файлов конфигурации, и пройдётся по ним, делая резервные копии этих вариантов.
Для удобства создан скрипт ltm который запускает, и, при этом может работать через символическую ссылку. То есть вы можете создать симлинк в папку bin и пользоваться командой ltm из любой точки файловой системы:

# ln -s /home/user/LinuxTimeMachine/ltm /usr/bin/ltm


Запустить все варианты из конфигурационных файлов, расположенных в ~/.config/LinuxTimeMachine/variants

$ ltm backup 

Запустить все варианты из 3-х конфигурационных файлов, указанных в командной строке:

$ ltm backup --conf ./ --conf ./additionalconf.yaml --conf ./mylocalpc.json

Запустить варианты с названиями “home_user” и “var_www” из файлов с настройками, лежащими в папке /etc/ltmbackup/

$ ltm backup --conf_dir /etc/ltmbackup --run home_user --run var_www

При загрузке нескольких конфигурационных файлов, варианты, которые они определяют будут объединены в один список, при этом, если будут попадаться варианты с одинаковыми названиями, оставляться будет тот, что прочитан последним. При указании папки с конфиг-файлами, они читаются оттуда в алфавитном порядке. При указании отдельных файлов с помощью опции –conf, они будут читаться в той последовательности, в которой указаны.

Очистка старых резервных копий

Для очистки старых резервных копий используется команда sweep, параметры у неё примерно такие же как у backup, за исключением того, что есть параметр –imitate, который заставляет команду sweep имитировать деятельность, то есть не удалять старые резервные копии, а просто сообщить, какие из них подлежать удалению, а какие – нет.
Очистка будет проведена только если у она настроена в варианте через параметр “sweep”.


Провести очистку для всех вариантов, для каких она сконфигурирована (варианты считываются из конфиг-файлов в папке ~/.config/LinuxTimeMachine/variants):

ltm sweep

Провести имитацию очистки с подробной информацией о действиях для всех вариантов, найденных в папке /etc/ltmbackup

ltm sweep --conf_dir /etc/ltmbackup --imitate --verbose

Конфигурация очистки:

Как уже указывалось ранее, очистка настраивается в поле “sweep” массива.


    'sweep': {
        "last day": "all",
        "last week": "2 per day",
        "last month": "1 per day",
        "last 3 months": "1 per 2 days",
        "last 6 months": "1 per week",
        "last year":"1 per month"

Каждый ключ массива указывает период времени начиная от последней резервной копии. Формат его: “last day|week|month|year”, где слово “last” – обязательно, означает “последние столько-то времени”. Вместо надо вставить число, сколько дней, месяцев или недель вы хотите задать. Если не указывать число, оно будет принято равным 1. Кстати, число может быть и дробным. Например, допустима конструкция “last 1.5 months”. После числа (либо вместо него) нужно указать “единицу измерения”, дни – “day”, недели – “week”, месяцы – “month”, годы – “year”. К “единице измерения” можно дописать букву “s” для множественного числа, то есть “days”, “weeks”, “months”, “years”. Это ни на что не влияет, но позволяет формировать более благозвучные периоды, к примеру, “last 2 days” – звучит логичнее чем “last 2 day”. Несмотря на то, что “единицы измерения” меньше чем “day” нет, задавая дробным числом, можно добиться любой длительности интервала, скажем, “last 0.041667 days” будет означать “последний час”.

“last 1 day” – последние сутки (здесь 1 указано явно)
“last 1.5 months” – последние 1.5 месяца”
“last 2 weeks” – последние 2 недели”
“last year” – последний год (здесь 1 не указано, но подразумевается)
“last 0.00069444 days”- последняя минута 🙂

Все периоды при чтении сортируются по возрастанию длительности, так что не важно, в какой последовательности они указаны.

Каждое значение массива – это указание того, сколько копий в единицу времени может существовать в заданный ранее период. Формат: “ per day|week|month|year”, где -количество копий (число), “per” – обязательное слово, означающее “каждые столько-то времени”, – количество дней, недель, месяцев или лет, за которые позволено иметь не более копий. Вместо можно ничего не писать, тогда это число будет принято равным 1. В конце указывается “единица измерения”, аналогично тому, как это делается в периоде (ключе массива), то есть day,week,month,year, окончание “s” также доступно для использования.

Также, есть специальное значение “all” – означающее, что сохранить нужно все резервные копии, очистка в данный период не нужна.

Особенности работы LinuxTimeMachine

Ограничение минимального промежутка

В параметре “min_timedelta” можно задать минимальный промежуток, чаще которого бэкапы не будут делаться (при запуске такой вариант, если он был сделан ближе к последней копией, чем min_timedelta, будет проигнорирован).

Эта возможность полезна чтобы не выносить информацию о частоте резервных копий в другое место (например в crontab). Допустим, у вас есть 2 папки, одну из которых вы хотите копировать раз в день, а другую – раз в час. Вы можете создать 2 задания в crontab, где одно будет выполнять резервное копирование только первой папки, а другое – только второй, указав им соответствующее время.
А с помощью опции “min_timedelta” можно задать интервалы “1 hours” и “1 days” соответственно у каждой из папок, а в crontab добавить только одно задание, запускающееся раз в час – тогда его хватит для обоих папок.

Формат параметра может быть таким:
1. Объект datetime.timedelta
2. Объект dict, содержащий поля с именами weeks, days, hours, minutes, seconds, milliseconds, microseconds, и значениями, равными количествам соответствующих интервалов. Этот объект используется при конструировании datetime.timedelta.
3. Строка:
“min_timedelta”: “1 weeks 5 days 11 hours 43 minutes 55 seconds 650 milliseconds 7901 microseconds”, все параметры суммируются. Любые из них можно пропускать, а числа могут быть любыми целыми числами. Например, можно задать интервал только через секунды: “86400 seconds” (это как раз 1 сутки). Разделителями между словами могут быть пробелы и запятые.

Работа по SSH

1) Все действия, которые нужно осуществлять на удалённых серверах, выполняются с помощью консольных команд через ssh. Например, для переименования папки на удалённой машине, программой выполняется консольная команда типа такой:

ssh user@host mv '/var/backup/in-progress-2016-07-11_10:20:30' '/var/backup/2016-07-11_10:20:20'

если же действие выполняется на локальном (по отношению к скрипту резервного копирования) сервере, выполняется та же самая команда но без вызова ssh.

2) Для подключения по ssh требуется имя хоста, которое во многих командах системы именуется sshhost, и всюду просто в сыром виде подставляется в команду ssh. Таким образом, в этот параметр можно указать просто “”, “”, а также добавить дополнительные параметры подключения, например, порт или путь к файлу закрытого ключа:
sshhost = “ –port=22000 -i ./keys/”, параметры, разумеется, должны быть валидными. При первом же доступе к серверу по ssh, будет проведена проверка на работоспособность параметров.

3) Подключение по SSH с указанием пароля пока не поддерживается.

4) При выполнении резервных копий между двумя удалёнными хостами, rsync запускается не на управляющем хосте, а на том, на который проводится сохранение данных. При этом вам достаточно иметь доступ по SSH с вашего управляющего хоста на исходный и конечный хосты. Прямого доступа по SSH между ними не требуется. Для подключения с хоста dest на хост src, между ними автоматически пробрасывается тоннель, проходящий через управляющих хост, через который и производится доступ из dest в src, который требуется rsync’у.
Для того, чтобы эта схема работала, и можно было подключиться из хоста dest к хосту src используя ключ, расположенный на управляющем хосте, этот ключ должен быть добавлен в агент авторизации ssh.
Делается это так (нужно указывать закрытый ключ):

ssh-add ~/.ssh/id_rsa

(Прямо сейчас функция копирования с одного удалённого хоста на другой, к сожалению, сломана)

Дампы Mysql

1) Дампы делаются при помощи mysqldump на тот же сервер, с которого осуществляется подключение к mysql, собственно резервные копии делаются как и для обычных файлов.

2) Список баз данных и таблиц, а также даты их изменения считываются с сервера с помощью консольного mysql-клиента.

3) В каждый sql-файл с дампом помещается специальная метка с информацией о последнем обновлении данной таблицы. Также, перед началом дампов запрашивается информация об обновлении таблиц.
Соответственно, заново делается дамп только тех таблиц, которые изменились с прошлого раза.
Опцией force_dump (приведена выше в примере конфига) можно заставить делать полные дампы всегда, независимо от изменений.

3.1) Для того, чтобы за одну консольную команду выяснить, какие таблицы нуждаются в обновлении, используется bash-скрипт, который автоматически создаётся на удалённом хосте опять же посредством SSH-команды, после чего запускается и собирает информацию в папке с дампами, а затем удаляется. Содержимое этого bash-файла можно посмотреть в исходном коде.

4) Дамп mysql-базы делается ДО копирования файлов, и подразумевается, что дамп вы будете делать в папку, которая включена в путь копируемых файлов (поскольку дампы sql копируются также как и все обычные файлы).

API для использование в своих скриптах

Вы можете написать свой скрипт резервного копирования с более сложной логикой, для этого достаточно подключить к вашему скрипту файл, и использовать объекты, находящиеся в нём.

Примеры того, что можно сделать

Допустим это будет файл, лежащий рядом с файлом

Работа с консолью и Mysql:

# Подключаем модуль backup:
import backup
# Подключим модуль datetime (пригодится пару раз)
import datetime

# Можно проверить, есть ли беспарольный доступ к ssh определённого сервера:
# если нет - будет брошено исключение
# Обычно эта проверка делается автоматически, если выполняется некий запрос SSH.

# Выполнить команду mv на удалённом хосте: (а можно и на локальном)"/tmp/1.txt", "/tmp/2.txt", "")

# Выполнить команду rm на удалённом (или локальном) хосте
backup.Console.rm("/tmp/1.txt", "")

# Получить имя папки для резервной копии из объекта datetime
# обычно имеет формат 2016-05-11_23:11:05
dirname = backup.Console.get_dirname_by_datetime(

# Получить дату и время по имени папки, которое должно быть
# в том же формате, что выдаёт функция get_dirname_by_datetime
date = backup.Console.get_datetime_by_dirname("2016-11-03_10:45:17")

# Получить список уже существующих папок с резервными копиями
# (возвращает list содержащий строки вида "2016-07-04_10:30:00",
# рассортированные по убыванию даты)
backupdirs = backup.console.get_backup_dirs(

# Создаём объект для работы с локальной или удалённой базой данных:
# (Все запросы делаются с помощью консольных команд, никаких драйверов Mysql 
# не нужно)
mysql = backup.Mysql("username", "password", "sshuser@sshhost")
# Можно получить список баз данных и таблиц в них из базы mysql:
dbs = mysql.get_dbs_and_tbls()
# Функция возвращает dict, ключи в котором - имена баз данных, а значения 
# - list'ы с именами таблиц

# Можно отфильтровать список баз данных и таблиц, с которыми мы 
# хотим работать:
# (В качестве первого параметра передаётся dict, аналогичный тому, 
# что возвращает get_dbs_and_tbls)
filtered_dbs = mysql.filter_dbs_and_tbls(dbs, 
        # включить в список все базы данных и все таблицы в них
        ["include", "*", "*"], 
        # исключить все базы данных, начинающихся с recovery_
        ["exclude", re.compile("^recovery_"), "*"] 
        # исключить из списка таблицы, заканчивающиеся на "_log"
        ["exclude", "*", re.compile("_log$")], 
        # включить таблицу userfull_data из базы recovery_common_db
        ["include", "recovery_common_db", "usefull_data"], 

# Можно просто выполнить произвольный sql-запрос 
# (ответ будет возвращён в виде строки, которую выдал консольный клиент mysql)
data = mysql.query("USE information_schema; SELECT * FROM `TABLES`")

# Можно запустить mysqldump с параметрами, указанными вручную 
# (довольно-таки не удобно):
# (Последний параметр передаётся в опции mysqldump без изменений)
mysql.call_dump(" databasename tablename ")

# Можно запустить само резервное копирование указанных баз данных 
mysql.dump_dbs(dbs, "/tmp/sql_dump_folder")

# Можно выяснить дату последнего изменения некоторой таблицы:
mytable_change_date = mysql.get_table_change_date("mytable")

# Можно получить данные о дате изменения таблицы:
# (Делается один запрос, после чего данные по всем таблицам и базам данных кэшируются)
table_update_date = mysql.get_table_change_date("databasename", "tablename")

# Можно получить данные о датах изменения таблиц, записанные в уже существующих дампах
# (используется для того, чтобы выяснить, изменилась ли таблица с момента последнего дампа)
mysql.get_table_change_date("database_name", "table_name")

Работа с конфигурацией и собственно timemechine:

import backup

# Считаем конфигурацию из одного файла
conf1 = backup.Conf.read_conf_file("../conf1.json")

# Считаем конфиграцию из нескольких файлов
confs = backup.Conf.read_conf_files(["../conf1.json", "../conf2.yaml", "../"])

# Считаем конфигурацию из всех файлов лежащих в папке
dir_confs = backup.Conf.read_conf_dir("/etc/ltmbackup/conf.d")

# Создадим объект rsync:
rsync = backup.Rsync()

# Напишем обработчик сообщений от rsync, взяв в качестве примера тот, который находится в backup.Rsync.default_callback:

def rsync_callback(data):
    if data['type'] == "progress":
            speed = data['speed']
            if speed > 1024**3:
                data['speed'] = str(round(speed / (1024**3), 2)) + "GB/s"
            elif speed > 1024**2:
                data['speed'] = str(round(speed / (1024**2), 2)) + "MB/s"
            elif speed > 1024:
                data['speed'] = str(round(speed / (1024), 2)) + "KB/s"
            print("Прогресс:{progress}%, скорость: {speed}".format(**data))
        except Exception as e:
    elif data['type'] == "message":
        print("Информация: " + data['message'])

# запустим резервное копирование вручную:
    {"path":"/home/user", "host":""}, 
    {"path":"/home/backuper/backup/user_home", "host":"backuper@back_server.local"},
    ["*.log", "*~", "*.bak"],

# Запустим резервное копирование вариантов, считанных ранее из конфигурационных файлов:
backup.go(dir_confs, rsync_callback)

Дальнейшие планы:

  • Приделать нормальную систему логирования и отображения сообщений, с разными уровнями детализации, пока она оставляет желать лучшего
  • Написать GUI для того чтобы удобно создавать конфиг-файлы
  • Написать скрипт для анализа резервных копий, с целью выявления слишком часто меняющихся файлов
  • Доработать скрипт, чтобы его можно было использовать не только на Linux-машинах
  • Хотя восстановление из бэкапов и сейчас не сложно сделать с помощью rsync (да или даже простого копирования файлов) и mysql, всё-таки стоит написать автоматизированный скрипт, который сумеет в одно нажатии восстановить указанную резервную копию.
  • Доработать ядро так, чтобы копирование различных типов данных, отличных от файлов, проводилось с помощью плагинов. Например тот же mysqldump должен стать плагином.

Если вдруг в скрипте обнаружатся ошибки, можно писать об этом в комментариях здесь, или создать баг в

So, what do you think ?