Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ The production stack includes automated PostgreSQL backups:
- **Schedule:** Daily at 2am (configurable via `BACKUP_SCHEDULE`)
- **Retention:** 7 daily, 4 weekly, 6 monthly
- **Location:** `backup_data` Docker volume
- **Filestore:** When the `odoo_data` volume is mounted on the backup service, attachments under `/odoo_data/filestore/<database>` are archived daily as `*_filestore_*.tar.gz` alongside the database dump

To restore a backup:

Expand Down
22 changes: 22 additions & 0 deletions docker/backup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
# Environment variables:
# PGHOST, PGPORT, PGUSER, PGPASSWORD, PGDATABASE (standard PostgreSQL vars)
# BACKUP_DIR (default: /backups)
# FILESTORE_SRC (default: /odoo_data/filestore/$PGDATABASE)
# BACKUP_KEEP_DAYS (default: 7)
# BACKUP_KEEP_WEEKS (default: 4)
# BACKUP_KEEP_MONTHS (default: 6)
Expand Down Expand Up @@ -52,15 +53,33 @@ ln -sf "${BACKUP_FILE}" "${DAILY_DIR}/${PGDATABASE:-openspp}_latest.dump"

echo "[$(date -Iseconds)] Daily backup complete: ${BACKUP_FILE}"

# Filestore backup (attachments, documents) when odoo data volume is mounted
FILESTORE_SRC="${FILESTORE_SRC:-/odoo_data/filestore/${PGDATABASE:-openspp}}"
FILESTORE_BACKUP_FILE="${PGDATABASE:-openspp}_filestore_${TIMESTAMP}.tar.gz"
if [ -d "${FILESTORE_SRC}" ]; then
echo "[$(date -Iseconds)] Starting filestore backup from ${FILESTORE_SRC}..."
tar -czf "${DAILY_DIR}/${FILESTORE_BACKUP_FILE}" -C "$(dirname "${FILESTORE_SRC}")" "$(basename "${FILESTORE_SRC}")"
ln -sf "${FILESTORE_BACKUP_FILE}" "${DAILY_DIR}/${PGDATABASE:-openspp}_filestore_latest.tar.gz"
echo "[$(date -Iseconds)] Filestore backup complete: ${FILESTORE_BACKUP_FILE}"
Comment on lines +61 to +63

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Running tar directly under set -e can cause the entire backup script to abort if any files are modified or deleted while the archiving process is running (which is common for active filestores). If tar exits with a non-zero status, the script will terminate immediately, preventing subsequent steps like weekly/monthly copies and old backup cleanup from executing.

Wrapping the tar command in an if statement safely handles potential non-zero exit codes without triggering set -e. Additionally, if the archiving fails, we should clean up any partial/corrupt tarball to prevent it from being treated as a valid backup or copied to weekly/monthly directories.

Suggested change
tar -czf "${DAILY_DIR}/${FILESTORE_BACKUP_FILE}" -C "$(dirname "${FILESTORE_SRC}")" "$(basename "${FILESTORE_SRC}")"
ln -sf "${FILESTORE_BACKUP_FILE}" "${DAILY_DIR}/${PGDATABASE:-openspp}_filestore_latest.tar.gz"
echo "[$(date -Iseconds)] Filestore backup complete: ${FILESTORE_BACKUP_FILE}"
if tar -czf "${DAILY_DIR}/${FILESTORE_BACKUP_FILE}" -C "$(dirname "${FILESTORE_SRC}")" "$(basename "${FILESTORE_SRC}")"; then
ln -sf "${FILESTORE_BACKUP_FILE}" "${DAILY_DIR}/${PGDATABASE:-openspp}_filestore_latest.tar.gz"
echo "[$(date -Iseconds)] Filestore backup complete: ${FILESTORE_BACKUP_FILE}"
else
echo "[$(date -Iseconds)] Error: Filestore backup failed. Cleaning up partial archive..." >&2
rm -f "${DAILY_DIR}/${FILESTORE_BACKUP_FILE}"
fi

else
echo "[$(date -Iseconds)] Filestore not found at ${FILESTORE_SRC}; skipping filestore backup"
fi

# Weekly backup (Sunday)
if [ "${DAY_OF_WEEK}" = "7" ]; then
cp "${DAILY_DIR}/${BACKUP_FILE}" "${WEEKLY_DIR}/"
if [ -f "${DAILY_DIR}/${FILESTORE_BACKUP_FILE}" ]; then
cp "${DAILY_DIR}/${FILESTORE_BACKUP_FILE}" "${WEEKLY_DIR}/"
fi
echo "[$(date -Iseconds)] Weekly backup saved"
fi

# Monthly backup (1st of month)
if [ "${DAY_OF_MONTH}" = "01" ]; then
cp "${DAILY_DIR}/${BACKUP_FILE}" "${MONTHLY_DIR}/"
if [ -f "${DAILY_DIR}/${FILESTORE_BACKUP_FILE}" ]; then
cp "${DAILY_DIR}/${FILESTORE_BACKUP_FILE}" "${MONTHLY_DIR}/"
fi
echo "[$(date -Iseconds)] Monthly backup saved"
fi

Expand All @@ -69,12 +88,15 @@ echo "[$(date -Iseconds)] Cleaning up old backups..."

# Remove daily backups older than BACKUP_KEEP_DAYS
find "${DAILY_DIR}" -name "*.dump" -type f -mtime +${BACKUP_KEEP_DAYS} -delete 2>/dev/null || true
find "${DAILY_DIR}" -name "*_filestore_*.tar.gz" -type f -mtime +${BACKUP_KEEP_DAYS} -delete 2>/dev/null || true

# Remove weekly backups older than BACKUP_KEEP_WEEKS weeks
find "${WEEKLY_DIR}" -name "*.dump" -type f -mtime +$((BACKUP_KEEP_WEEKS * 7)) -delete 2>/dev/null || true
find "${WEEKLY_DIR}" -name "*_filestore_*.tar.gz" -type f -mtime +$((BACKUP_KEEP_WEEKS * 7)) -delete 2>/dev/null || true

# Remove monthly backups older than BACKUP_KEEP_MONTHS months (approximate: 30 days per month)
find "${MONTHLY_DIR}" -name "*.dump" -type f -mtime +$((BACKUP_KEEP_MONTHS * 30)) -delete 2>/dev/null || true
find "${MONTHLY_DIR}" -name "*_filestore_*.tar.gz" -type f -mtime +$((BACKUP_KEEP_MONTHS * 30)) -delete 2>/dev/null || true

# Report disk usage
echo "[$(date -Iseconds)] Backup sizes:"
Expand Down
1 change: 1 addition & 0 deletions docker/docker-compose.nginx.yml
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,7 @@ services:
- ./backup.sh:/backup.sh:ro,z
- ./backup-entrypoint.sh:/backup-entrypoint.sh:ro,z
- backup_data:/backups:rw,z
- odoo_data:/odoo_data:ro,z
networks:
- openspp-prod
restart: unless-stopped
Expand Down
1 change: 1 addition & 0 deletions docker/docker-compose.production.yml
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,7 @@ services:
- ./backup.sh:/backup.sh:ro
- ./backup-entrypoint.sh:/backup-entrypoint.sh:ro
- backup_data:/backups
- odoo_data:/odoo_data:ro
networks:
- openspp-prod
restart: always
Expand Down