Система автоматического резервного копирования для Chatwoot, которая создает полные бекапы всех данных, включая базу данных PostgreSQL, Redis, файлы медиа и конфигурации.
ВАЖНО: Скрипт автоматически включает ВСЁ содержимое папки `/opt/chatwoot/chatwoot_data/` - любые новые файлы и папки попадут в бекап автоматически.
| Компонент | Описание | Расположение |
|---|---|---|
| База данных PostgreSQL | Полный дамп схемы и данных | `/opt/chatwoot/chatwoot_data/postgres_data/` |
| Redis данные | Дамп всех ключей и значений | `/opt/chatwoot/chatwoot_data/redis_data/` |
| Медиа файлы | Загруженные файлы пользователей | `/opt/chatwoot/chatwoot_data/storage/` |
| Конфигурация времени | Настройки часового пояса | `/opt/chatwoot/chatwoot_data/config/` |
| Docker конфигурация | docker-compose.yml и .env | `/opt/chatwoot/chatwoot_dock/` |
| Все новые папки | Автоматически | `/opt/chatwoot/chatwoot_data/*` |
Преимущество: При добавлении новых папок в `/opt/chatwoot/chatwoot_data/` они автоматически попадут в следующий бекап без изменения скрипта.
/opt/
├── chatwoot/
│ ├── chatwoot_dock/ # Docker Compose проект
│ │ ├── docker-compose.yml # Конфигурация контейнеров
│ │ └── .env # Переменные окружения
│ └── chatwoot_data/ # Все данные приложения
│ ├── postgres_data/ # База данных
│ ├── redis_data/ # Кэш
│ ├── storage/ # Медиа файлы
│ └── config/ # Пользовательские конфигурации
├── backups/
│ └── chatwoot/ # Папка для бекапов
└── scripts/
└── chatwoot-backup.sh # Скрипт резервного копирования
# Создание необходимых папок sudo mkdir -p /opt/scripts sudo mkdir -p /opt/backups/chatwoot sudo chown -R $USER:$USER /opt/backups sudo chown -R $USER:$USER /opt/scripts
Создайте файл `/opt/scripts/chatwoot-backup.sh`:
sudo nano /opt/scripts/chatwoot-backup.sh
Вставьте следующий код, заменив пароли на свои:
#!/bin/bash # Скрипт резервного копирования Chatwoot # Запуск: /opt/scripts/chatwoot-backup.sh # Настройки COMPOSE_FILE="/opt/chatwoot/chatwoot_dock/docker-compose.yml" CHATWOOT_DIR="/opt/chatwoot/chatwoot_dock" BACKUP_DIR="/opt/backups/chatwoot" DATE=$(date +"%Y%m%d_%H%M%S") BACKUP_NAME="chatwoot_backup_${DATE}" RETENTION_DAYS=10 # Храним бекапы 10 дней # Настройки базы данных - ИЗМЕНИТЕ НА СВОИ DB_CONTAINER="chatwoot_postgres" DB_USER="postgres" DB_PASSWORD="YOUR_POSTGRES_PASSWORD" # Замените на ваш пароль DB_NAME="chatwoot_production" # Настройки Redis - ИЗМЕНИТЕ НА СВОИ REDIS_CONTAINER="chatwoot_redis" REDIS_PASSWORD="YOUR_REDIS_PASSWORD" # Замените на ваш пароль # Логирование LOG_FILE="${BACKUP_DIR}/backup.log" # Функция логирования log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE" } # Создаем директорию для бекапов если не существует mkdir -p "$BACKUP_DIR" log "=== Начало резервного копирования Chatwoot ===" # Проверяем что compose файл существует if [ ! -f "$COMPOSE_FILE" ]; then log "ОШИБКА: Файл $COMPOSE_FILE не найден!" exit 1 fi # Переходим в директорию с Chatwoot cd "$CHATWOOT_DIR" || { log "ОШИБКА: Не удалось перейти в директорию $CHATWOOT_DIR" exit 1 } # Проверяем что контейнеры запущены log "Проверяем статус контейнеров..." if ! docker-compose -f "$COMPOSE_FILE" ps | grep -q "Up"; then log "ОШИБКА: Контейнеры не запущены! Запустите их перед созданием бекапа." exit 1 fi # Показываем что будет включено в бекап log "Анализируем содержимое /opt/chatwoot/chatwoot_data/..." CHATWOOT_DATA_CONTENTS=$(ls -la /opt/chatwoot/chatwoot_data/ 2>/dev/null | grep -v "^total" | grep -v "^\." | awk '{print $9}' | grep -v "^$" | tr '\n' ' ') log "Будет включено в бекап: $CHATWOOT_DATA_CONTENTS" # Создаем дамп базы данных (пока контейнеры работают) log "Создаем дамп базы данных..." DB_DUMP_PATH="${BACKUP_DIR}/${BACKUP_NAME}_database.sql" # Экспортируем пароль для pg_dump export PGPASSWORD="$DB_PASSWORD" docker exec "$DB_CONTAINER" pg_dump -U "$DB_USER" -d "$DB_NAME" --no-password --verbose > "$DB_DUMP_PATH" 2>> "$LOG_FILE" if [ $? -eq 0 ] && [ -s "$DB_DUMP_PATH" ]; then log "Дамп базы данных создан: $DB_DUMP_PATH" # Получаем размер дампа DB_DUMP_SIZE=$(du -h "$DB_DUMP_PATH" | cut -f1) log "Размер дампа БД: $DB_DUMP_SIZE" else log "ОШИБКА: Не удалось создать дамп базы данных или файл пуст" rm -f "$DB_DUMP_PATH" exit 1 fi # Создаем дамп Redis log "Создаем дамп Redis..." REDIS_DUMP_PATH="${BACKUP_DIR}/${BACKUP_NAME}_redis.rdb" # Создаем дамп внутри контейнера во временный файл REDIS_TEMP_PATH="/tmp/redis_dump.rdb" docker exec "$REDIS_CONTAINER" redis-cli -a "$REDIS_PASSWORD" --rdb "$REDIS_TEMP_PATH" 2>> "$LOG_FILE" if [ $? -eq 0 ]; then # Копируем дамп из контейнера docker cp "$REDIS_CONTAINER:$REDIS_TEMP_PATH" "$REDIS_DUMP_PATH" 2>> "$LOG_FILE" if [ -f "$REDIS_DUMP_PATH" ]; then log "Дамп Redis создан: $REDIS_DUMP_PATH" REDIS_DUMP_SIZE=$(du -h "$REDIS_DUMP_PATH" | cut -f1) log "Размер дампа Redis: $REDIS_DUMP_SIZE" # Удаляем временный файл из контейнера docker exec "$REDIS_CONTAINER" rm -f "$REDIS_TEMP_PATH" 2>/dev/null else log "ПРЕДУПРЕЖДЕНИЕ: Файл дампа Redis не найден" REDIS_DUMP_SIZE="0B" fi else log "ПРЕДУПРЕЖДЕНИЕ: Не удалось создать дамп Redis" REDIS_DUMP_SIZE="0B" fi # Очищаем переменную окружения unset PGPASSWORD # Останавливаем контейнеры log "Останавливаем контейнеры Chatwoot..." docker-compose -f "$COMPOSE_FILE" down if [ $? -eq 0 ]; then log "Контейнеры успешно остановлены" else log "ОШИБКА: Не удалось остановить контейнеры" exit 1 fi # Ждем пару секунд для полной остановки sleep 5 # Создаем архив с данными - АВТОМАТИЧЕСКИ ВКЛЮЧАЕТ ВСЁ СОДЕРЖИМОЕ chatwoot_data log "Создаем архив данных (включает ВСЁ содержимое chatwoot_data)..." BACKUP_PATH="${BACKUP_DIR}/${BACKUP_NAME}.tar.gz" # Создаем архив с автоматическим включением ВСЕГО содержимого /opt/chatwoot/chatwoot_data/ tar -czf "$BACKUP_PATH" \ --exclude='*.log' \ --exclude='**/lost+found' \ --exclude='**/.tmp' \ --exclude='**/tmp/*' \ --exclude='**/.cache' \ -C "$BACKUP_DIR" "${BACKUP_NAME}_database.sql" \ $([ -f "$REDIS_DUMP_PATH" ] && echo "-C $BACKUP_DIR ${BACKUP_NAME}_redis.rdb") \ -C "/opt/chatwoot" \ chatwoot_data/ \ -C "$CHATWOOT_DIR" \ docker-compose.yml \ .env if [ $? -eq 0 ]; then log "Архив создан: $BACKUP_PATH" # Получаем размер архива BACKUP_SIZE=$(du -h "$BACKUP_PATH" | cut -f1) log "Размер архива: $BACKUP_SIZE" # Удаляем отдельные файлы дампов, так как они теперь в архиве rm -f "$DB_DUMP_PATH" [ -f "$REDIS_DUMP_PATH" ] && rm -f "$REDIS_DUMP_PATH" log "Отдельные файлы дампов удалены (включены в архив)" # Показываем что попало в архив log "Содержимое архива (основные папки и файлы):" tar -tzf "$BACKUP_PATH" | head -20 | while read file; do log " $file" done TOTAL_FILES=$(tar -tzf "$BACKUP_PATH" | wc -l) log "Общее количество файлов в архиве: $TOTAL_FILES" else log "ОШИБКА: Не удалось создать архив" # Пытаемся запустить контейнеры даже при ошибке log "Запускаем контейнеры после ошибки..." docker-compose -f "$COMPOSE_FILE" up -d exit 1 fi # Запускаем контейнеры обратно log "Запускаем контейнеры Chatwoot..." docker-compose -f "$COMPOSE_FILE" up -d if [ $? -eq 0 ]; then log "Контейнеры успешно запущены" else log "ОШИБКА: Не удалось запустить контейнеры" exit 1 fi # Ждем запуска сервисов sleep 15 # Проверяем что все контейнеры работают log "Проверяем статус контейнеров..." RUNNING_CONTAINERS=$(docker-compose -f "$COMPOSE_FILE" ps --services --filter "status=running" | wc -l) TOTAL_CONTAINERS=$(docker-compose -f "$COMPOSE_FILE" ps --services | wc -l) if [ "$RUNNING_CONTAINERS" -eq "$TOTAL_CONTAINERS" ]; then log "Все контейнеры ($RUNNING_CONTAINERS/$TOTAL_CONTAINERS) работают корректно" else log "ПРЕДУПРЕЖДЕНИЕ: Работают только $RUNNING_CONTAINERS из $TOTAL_CONTAINERS контейнеров" fi # Проверяем доступность базы данных log "Проверяем подключение к базе данных..." export PGPASSWORD="$DB_PASSWORD" if docker exec "$DB_CONTAINER" pg_isready -U "$DB_USER" -d "$DB_NAME" > /dev/null 2>&1; then log "База данных доступна и готова к работе" else log "ПРЕДУПРЕЖДЕНИЕ: База данных может быть недоступна" fi # Проверяем что база данных Chatwoot содержит нужные таблицы log "Проверяем структуру базы данных Chatwoot..." if docker exec "$DB_CONTAINER" psql -U "$DB_USER" -d "$DB_NAME" -c "\dt" 2>/dev/null | grep -q "accounts\|users\|conversations"; then log "База данных Chatwoot содержит основные таблицы" else log "ПРЕДУПРЕЖДЕНИЕ: База данных Chatwoot может быть неполной" fi # Проверяем Redis контейнер log "Проверяем Redis..." if docker exec "$REDIS_CONTAINER" redis-cli -a "$REDIS_PASSWORD" ping 2>/dev/null | grep -q "PONG"; then log "Redis работает корректно" else log "ПРЕДУПРЕЖДЕНИЕ: Redis может работать некорректно" fi # Проверяем Rails контейнер log "Проверяем Rails приложение..." if docker exec chatwoot_rails bundle exec rails runner "puts 'Chatwoot OK'" 2>/dev/null | grep -q "Chatwoot OK"; then log "Rails приложение работает корректно" else log "ПРЕДУПРЕЖДЕНИЕ: Rails приложение может работать некорректно" fi unset PGPASSWORD # Удаляем старые бекапы log "Удаляем бекапы старше $RETENTION_DAYS дней..." DELETED_COUNT=$(find "$BACKUP_DIR" -name "chatwoot_backup_*.tar.gz" -mtime +$RETENTION_DAYS -delete -print | wc -l) log "Удалено старых бекапов: $DELETED_COUNT" # Показываем статистику бекапов BACKUP_COUNT=$(ls -1 "${BACKUP_DIR}"/chatwoot_backup_*.tar.gz 2>/dev/null | wc -l) TOTAL_SIZE=$(du -sh "${BACKUP_DIR}" 2>/dev/null | cut -f1) log "Общее количество бекапов: $BACKUP_COUNT" log "Общий размер папки бекапов: $TOTAL_SIZE" # Получаем информацию о свободном месте на диске BACKUP_DISK_USAGE=$(df -h "$BACKUP_DIR" | awk 'NR==2 {print $4 " свободно из " $2 " (" $5 " занято)"}') ROOT_DISK_USAGE=$(df -h / | awk 'NR==2 {print $4 " свободно из " $2 " (" $5 " занято)"}') log "Свободное место на диске бекапов: $BACKUP_DISK_USAGE" if [ "$BACKUP_DIR" != "/" ] && [ "$(df "$BACKUP_DIR" | awk 'NR==2 {print $1}')" != "$(df / | awk 'NR==2 {print $1}')" ]; then log "Свободное место на корневом диске: $ROOT_DISK_USAGE" fi # Получаем процент заполнения диска с бекапами для предупреждений BACKUP_DISK_PERCENT=$(df "$BACKUP_DIR" | awk 'NR==2 {print $5}' | sed 's/%//') if [ "$BACKUP_DISK_PERCENT" -gt 90 ]; then log "⚠️ ПРЕДУПРЕЖДЕНИЕ: Диск заполнен более чем на 90%!" elif [ "$BACKUP_DISK_PERCENT" -gt 80 ]; then log "⚠️ ВНИМАНИЕ: Диск заполнен более чем на 80%" fi log "=== Резервное копирование завершено успешно ===" # Формируем сообщение для Telegram TELEGRAM_MESSAGE="✅ Chatwoot backup completed 📁 Backup: $BACKUP_NAME ($BACKUP_SIZE) 💾 DB dump: $DB_DUMP_SIZE 🗄️ Redis dump: $REDIS_DUMP_SIZE 📊 Total backups: $BACKUP_COUNT 📄 Files in archive: $TOTAL_FILES 💿 Disk space: $BACKUP_DISK_USAGE" # Добавляем предупреждение о месте, если нужно if [ "$BACKUP_DISK_PERCENT" -gt 90 ]; then TELEGRAM_MESSAGE="$TELEGRAM_MESSAGE ⚠️ WARNING: Disk >90% full!" elif [ "$BACKUP_DISK_PERCENT" -gt 80 ]; then TELEGRAM_MESSAGE="$TELEGRAM_MESSAGE ⚠️ ATTENTION: Disk >80% full" fi # Отправляем уведомление в Telegram (опционально) # Замените на свой BOT_TOKEN и CHAT_ID curl -s -X POST "https://api.telegram.org/bot[YOUR_BOT_TOKEN]/sendMessage" \ -d chat_id="[YOUR_CHAT_ID]" \ -d text="$TELEGRAM_MESSAGE" > /dev/null exit 0
Обязательно измените:
# Делаем скрипт исполняемым sudo chmod +x /opt/scripts/chatwoot-backup.sh # Проверяем права ls -la /opt/scripts/chatwoot-backup.sh
# Запуск тестового бекапа sudo /opt/scripts/chatwoot-backup.sh # Проверка результата ls -la /opt/backups/chatwoot/ cat /opt/backups/chatwoot/backup.log
# Открываем crontab sudo crontab -e # Добавляем задачу (бекап каждый день в 2:00 ночи) 0 2 * * * /opt/scripts/chatwoot-backup.sh >> /var/log/chatwoot-backup-cron.log 2>&1 # Сохраняем и выходим (Ctrl+X, Y, Enter)
# Каждый день в 3:00 0 3 * * * /opt/scripts/chatwoot-backup.sh # Каждые 12 часов (в 6:00 и 18:00) 0 6,18 * * * /opt/scripts/chatwoot-backup.sh # Каждый день в 1:30 ночи 30 1 * * * /opt/scripts/chatwoot-backup.sh # Только в выходные в 4:00 0 4 * * 6,0 /opt/scripts/chatwoot-backup.sh
# Проверить статус cron sudo systemctl status cron # Посмотреть активные задачи sudo crontab -l # Посмотреть логи cron sudo tail -f /var/log/chatwoot-backup-cron.log
1. Создайте бота: Отправьте `/start` боту @BotFather в Telegram 2. Получите токен: Сохраните Bot Token 3. Узнайте Chat ID: Отправьте сообщение боту, затем откройте:
`https://api.telegram.org/bot[YOUR_BOT_TOKEN]/getUpdates`
# Тест отправки сообщения (замените токен и chat_id) curl -s -X POST "https://api.telegram.org/bot[YOUR_BOT_TOKEN]/sendMessage" \ -d chat_id="[YOUR_CHAT_ID]" \ -d text="Test message from Chatwoot backup script"
# Проверка места в папке бекапов df -h /opt/backups/chatwoot/ # Размер всех бекапов du -sh /opt/backups/chatwoot/ # Список всех бекапов с размерами ls -lah /opt/backups/chatwoot/chatwoot_backup_*.tar.gz
# Остановите Chatwoot cd /opt/chatwoot/chatwoot_dock docker-compose down # Найдите нужный бекап ls -la /opt/backups/chatwoot/ # Извлеките архив (замените на ваш файл) cd /tmp tar -xzf /opt/backups/chatwoot/chatwoot_backup_YYYYMMDD_HHMMSS.tar.gz # Восстановите данные sudo rm -rf /opt/chatwoot/chatwoot_data/* sudo cp -r chatwoot_data/* /opt/chatwoot/chatwoot_data/ # Восстановите конфигурацию (если нужно) cp docker-compose.yml /opt/chatwoot/chatwoot_dock/ cp .env /opt/chatwoot/chatwoot_dock/ # Запустите Chatwoot cd /opt/chatwoot/chatwoot_dock docker-compose up -d
# Найдите дамп базы в архиве tar -tzf /opt/backups/chatwoot/chatwoot_backup_YYYYMMDD_HHMMSS.tar.gz | grep database.sql # Извлеките дамп tar -xzf /opt/backups/chatwoot/chatwoot_backup_YYYYMMDD_HHMMSS.tar.gz chatwoot_backup_YYYYMMDD_HHMMSS_database.sql # Восстановите в контейнер (Chatwoot должен быть запущен) export PGPASSWORD="YOUR_POSTGRES_PASSWORD" cat chatwoot_backup_YYYYMMDD_HHMMSS_database.sql | docker exec -i chatwoot_postgres psql -U postgres -d chatwoot_production unset PGPASSWORD
Причина: Неправильные имена контейнеров
Решение: Проверьте имена контейнеров:
docker ps # Обновите переменные DB_CONTAINER и REDIS_CONTAINER в скрипте
Причина: Неправильный пароль или имя базы
Решение: Проверьте настройки в `.env` файле:
cat /opt/chatwoot/chatwoot_dock/.env | grep -i postgres # Обновите переменные в скрипте согласно .env
Причина: Заполнен диск
Решение:
# Удалите старые бекапы вручную find /opt/backups/chatwoot/ -name "chatwoot_backup_*.tar.gz" -mtime +7 -delete # Уменьшите RETENTION_DAYS в скрипте
# Основной лог скрипта tail -f /opt/backups/chatwoot/backup.log # Лог cron tail -f /var/log/chatwoot-backup-cron.log # Логи Docker контейнеров docker logs chatwoot_postgres docker logs chatwoot_redis
В скрипте измените:
RETENTION_DAYS=10 # Храним бекапы 10 дней
В скрипте измените:
BACKUP_DIR="/opt/backups/chatwoot"
Рекомендация: Для больших проектов рассмотрите возможность хранения бекапов на отдельном диске или в облачном хранилище.
# Ограничить доступ только root sudo chown root:root /opt/scripts/chatwoot-backup.sh sudo chmod 700 /opt/scripts/chatwoot-backup.sh # Проверить права ls -la /opt/scripts/chatwoot-backup.sh
Для дополнительной безопасности можно добавить шифрование:
# Добавьте в скрипт после создания архива: gpg --symmetric --cipher-algo AES256 "$BACKUP_PATH" rm "$BACKUP_PATH" # Удаляем незашифрованную версию
Данный скрипт обеспечивает:
- Полное резервное копирование всех данных Chatwoot - Автоматическое включение новых файлов и папок в `/opt/chatwoot/chatwoot_data/` - Безопасное выполнение с остановкой и запуском контейнеров - Очистку старых бекапов для экономии места - Детальное логирование всех операций - Уведомления в Telegram о статусе бекапа - Мониторинг места на диске с предупреждениями
Результат: Надежная система резервного копирования, которая автоматически адаптируется к изменениям в структуре данных Chatwoot.
Инструкция протестирована на Ubuntu Server с Docker Compose. Регулярно проверяйте работоспособность бекапов и процедуры восстановления.