๐ BookStack Backup Guide (Using systemd, with Auto-Cleanup)
This document explains how to back up a BookStack instance using a backup script, a systemd service, and a systemd timer, plus an automatic cleanup timer that deletes old backups.
It's work only for bookstack in docker if you have a bare metal install you nee to modify the script
No cron is used โ fully systemd-native.
โ What Gets Backed Up?
A full BookStack backup requires:
- Database dump (MySQL/MariaDB) โ contains all pages, books, users, settings
- Application files โ BookStack codebase
- Upload files โ images + attachments
- Storage files โ internal uploads, custom icons, etc.
๐ 1. Install the Backup Script
Create the script at:
/usr/local/bin/bookstack-backup.sh
Script content
Update
DB_NAME,DB_USER,DB_PASS,BACKUP_DIR, andBOOKSTACK_DIRto match your setup.
#!/usr/bin/env bash
set -euo pipefail
# === CONFIG ===
BACKUP_DIR="/backup/bookstack"
BOOKSTACK_DIR=# Docker container + DB creds
DB_CONTAINER="/var/www/bookstack"bookstack-db"
DB_NAME="bookstack"
DB_USER="bookstack"
DB_PASS="YOUR_DB_PASSWORD"
# Host paths for bind mounts (adjust to yours)
APP_DIR="/opt/bookstack/app" # optional if you want the app code
UPLOADS_DIR="/opt/bookstack/uploads"
STORAGE_DIR="/opt/bookstack/storage"
DATE=$(date +"%Y-%m-%d_%H-%M-%S")
TARGET_DIR="$BACKUP_DIR/{BACKUP_DIR}/$DATE"{DATE}"
mkdir -p "$TARGET_DIR"
echo "[*] $(date) - Dumping database.database from container ${DB_CONTAINER}..."
docker exec "${DB_CONTAINER}" \
sh -c "mysqldump -u"u\"$DB_USER"{DB_USER}\" -p"$DB_PASS" p\"$DB_NAME"{DB_PASS}\" \"${DB_NAME}\"" \
> "$TARGET_DIR/{TARGET_DIR}/db.sql"
echo "[*] $(date) - Copying BookStack files.upload/storage data..."
# Only copy app dir if it exists (in case you don't map it)
if [ -d "$APP_DIR" ]; then
rsync -a "$BOOKSTACK_DIR"APP_DIR" "$TARGET_DIR/{TARGET_DIR}/app"
fi
rsync -a "$UPLOADS_DIR" "${TARGET_DIR}/uploads"
rsync -a "$STORAGE_DIR" "${TARGET_DIR}/storage"
echo "[*] $(date) - Creating archive..."
cd "$BACKUP_DIR"
tar -czf "bookstack_$DATE.{DATE}.tar.gz" "$DATE"
echo "[*] $(date) - Cleaning up temp folder..."
rm -rf "$TARGET_DIR"
echo "[+] $(date) - Backup complete: $BACKUP_DIR/{BACKUP_DIR}/bookstack_$DATE.{DATE}.tar.gz"
Make it executable:
chmod +x /usr/local/bin/bookstack-backup.sh
๐ ๏ธ 2. systemd Service (Backup Runner)
Create:
/etc/systemd/system/bookstack-backup.service
With:
[Unit]
Description=BookStack backup
Wants=network-online.target
After=network-online.target mariadb.service mysql.service
[Service]
Type=oneshot
ExecStart=/usr/local/bin/bookstack-backup.sh
User=root
Group=root
# Optional: keep system responsive
Nice=10
IOSchedulingClass=best-effort
IOSchedulingPriority=7
[Install]
WantedBy=multi-user.target
Test it manually:
systemctl daemon-reload
systemctl start bookstack-backup.service
journalctl -u bookstack-backup.service -e
โฐ 3. systemd Timer (Daily Backup at 02:00)
Create:
/etc/systemd/system/bookstack-backup.timer
With:
[Unit]
Description=Daily BookStack backup at 02:00
[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true
Unit=bookstack-backup.service
[Install]
WantedBy=timers.target
Enable the timer:
systemctl daemon-reload
systemctl enable --now bookstack-backup.timer
Check timers:
systemctl list-timers | grep bookstack
๐งน 4. Auto-Delete Old Backups (Cleanup Timer)
This step adds:
- a cleanup script that deletes backup files older than 30 days
- a systemd service to run the script
- a systemd timer to schedule cleanup (default: daily at 03:00)
You can adjust the retention days and schedule as needed.
4.1 Cleanup Script
Create:
/usr/local/bin/bookstack-backup-cleanup.sh
With:
#!/usr/bin/env bash
set -euo pipefail
# === CONFIG ===
BACKUP_DIR="/backup/bookstack"
RETENTION_DAYS=30
echo "[*] $(date) - Cleaning up BookStack backups older than ${RETENTION_DAYS} days in ${BACKUP_DIR}..."
if [ ! -d "$BACKUP_DIR" ]; then
echo "[!] Backup directory ${BACKUP_DIR} does not exist, nothing to clean."
exit 0
fi
# Delete .tar.gz archives older than RETENTION_DAYS
find "$BACKUP_DIR" -maxdepth 1 -type f -name "bookstack_*.tar.gz" -mtime +$RETENTION_DAYS -print -delete
# Optionally clean leftover dated directories (should normally not exist)
find "$BACKUP_DIR" -maxdepth 1 -type d -regex '.*/[0-9-]\{10\}_[0-9:-]\{8\}' -mtime +$RETENTION_DAYS -print -exec rm -rf {} +
echo "[+] $(date) - Cleanup complete."
Make it executable:
chmod +x /usr/local/bin/bookstack-backup-cleanup.sh
To change retention, edit
RETENTION_DAYS=30(for example, to 7 for one week).
4.2 Cleanup systemd Service
Create:
/etc/systemd/system/bookstack-backup-cleanup.service
With:
[Unit]
Description=Cleanup old BookStack backups
[Service]
Type=oneshot
ExecStart=/usr/local/bin/bookstack-backup-cleanup.sh
User=root
Group=root
Nice=10
IOSchedulingClass=best-effort
IOSchedulingPriority=7
[Install]
WantedBy=multi-user.target
Test it:
systemctl daemon-reload
systemctl start bookstack-backup-cleanup.service
journalctl -u bookstack-backup-cleanup.service -e
4.3 Cleanup systemd Timer (Daily at 03:00)
Create:
/etc/systemd/system/bookstack-backup-cleanup.timer
With:
[Unit]
Description=Daily cleanup of old BookStack backups at 03:00
[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true
Unit=bookstack-backup-cleanup.service
[Install]
WantedBy=timers.target
Enable the timer:
systemctl daemon-reload
systemctl enable --now bookstack-backup-cleanup.timer
Check:
systemctl list-timers | grep bookstack-backup-cleanup
๐ Restore Procedure
From a backup file like:
/backup/bookstack/bookstack_YYYY-MM-DD_HH-MM-SS.tar.gz
1. Extract the backup archive
cd /backup/bookstack
tar -xzf bookstack_YYYY-MM-DD_HH-MM-SS.tar.gz
cd YYYY-MM-DD_HH-MM-SS
2. Restore BookStack files
rsync -a app/ /var/www/bookstack/
chown -R www-data:www-data /var/www/bookstack
3. Restore the database
mysql -u bookstack -p bookstack < db.sql
Restart PHP-FPM / webserver if needed:
systemctl restart php-fpm apache2 nginx
(Use the service(s) appropriate for your setup.)
๐ Recommended Extras
- Sync
/backup/bookstackto offsite storage (S3, Minio, Backblaze, rsync to another host) - Keep at least 7โ30 daily backups depending on your risk tolerance
- If using ZFS/Btrfs:
- Combine filesystem snapshots with this logical backup
- Optionally snapshot the backup dataset itself
โ Summary
| Component | Backed Up / Managed By |
|---|---|
| Database | mysqldump in bookstack-backup.sh |
| App Files | rsync in bookstack-backup.sh |
| Uploads & Storage | Included in app directory |
| Backup Automation | bookstack-backup.service + .timer |
| Cleanup Automation | bookstack-backup-cleanup.service + .timer |
| Retention | RETENTION_DAYS in cleanup script |
This setup is:
- Fully systemd-native (no cron)
- Includes daily backup + daily cleanup
- Easy to tweak for different times / retention periods.