Magento Maintenance Scripts in Bash: Deploy, Backup, Cache and Logs
AI generated
Magento · Bash · Deploy · Backup · Cache · Log Rotation
Magento Maintenance Scripts in Bash: Deploy, Backup, Cache and Logs
Automating bin/magento, rsync, cache flushing and log rotation

Magento maintenance scripts in Bash make the difference between manual deploy chaos and reliable automation. The right sequence of bin/magento commands, rsync backups, cache flushing and log rotation prevents the errors that creep in when deploy steps are run manually in the wrong order.

16 min read bin/magento · rsync · Cache Flush · Log Rotation · Backup Magento 2.4.x · PHP 8.x · Docker · Linux

1. Why Magento maintenance scripts in Bash are essential

A Magento deploy without a structured Magento maintenance script in Bash is error prone. The correct deploy sequence for Magento 2 covers at least ten steps that must run in the right order: Composer update, setup upgrade, compilation, static content deploy, cache flush and permission fixes. Anyone running these steps by hand tends to skip one or two under time pressure, which leads to cache inconsistencies, missing generated classes or static assets that never get refreshed. A Magento maintenance script in Bash turns the sequence into something reproducible and atomic: either every step completes, or the script stops and reports exactly which step failed.

Beyond deployment, regular backup, cache and log maintenance tasks are mandatory in any Magento production environment. Magento maintenance scripts for these jobs run as cron jobs and make sure backups are created daily, logs never grow without bound, and the cache is cleared correctly after schema changes. The shell is the natural tool for this work: it runs directly on the server, has no dependency on external systems, and gives full control over the execution environment.

2. The correct deploy sequence in Bash

The Magento deploy sequence has a fixed order that must not be changed. A Magento maintenance script in Bash implements that sequence with an explicit failure stop at every step: set -euo pipefail at the top of the script guarantees that a failed bin/magento setup:upgrade aborts the entire script immediately instead of continuing with a stale database schema. The order is: Composer first, then setup:upgrade, then di:compile, then the CSS build, then deleting static files, then setup:static-content:deploy, then cache:flush.

A common mistake in manual deployments and simple Magento maintenance scripts: the "delete static files" step (rm -rf var/view_preprocessed/* pub/static/frontend/*) gets forgotten or runs in the wrong place. If static files are not deleted before the new setup:static-content:deploy runs, Magento keeps serving old assets from the cache and new content stays invisible. The deploy script enforces this order. The static content deploy also needs the correct locale and theme identifier; mistakes here produce 404 errors for CSS and JS files.


#!/usr/bin/env bash
# magento-deploy.sh: full Magento 2 deploy sequence with error handling
set -euo pipefail
IFS=$'\n\t'

readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly MAGENTO_ROOT="${MAGENTO_ROOT:-${SCRIPT_DIR}/src}"
readonly DEPLOY_LOCALE="${DEPLOY_LOCALE:-de_DE}"
readonly DEPLOY_THEME="${DEPLOY_THEME:-Mironsoft/default}"
readonly LOG_FILE="/var/log/magento/deploy-$(date +%Y%m%d-%H%M%S).log"

log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE"; }
die() { log "[ERROR] $*"; exit 1; }

# Redirect all output to log file AND terminal
exec > >(tee -a "$LOG_FILE") 2>&1

log "=== Magento Deploy: $DEPLOY_THEME ($DEPLOY_LOCALE) ==="

# Step 1: Composer install (no dev dependencies in production)
log "[1/7] Composer install"
bin/composer install --no-dev --optimize-autoloader || die "Composer failed"

# Step 2: Database schema and data upgrades
log "[2/7] Setup upgrade"
bin/magento setup:upgrade --keep-generated || die "setup:upgrade failed"

# Step 3: Dependency injection compilation
log "[3/7] DI compile"
bin/magento setup:di:compile || die "di:compile failed"

# Step 4: Build CSS (Tailwind/Hyva) BEFORE deleting static files
log "[4/7] Build CSS"
bin/npm --prefix "${MAGENTO_ROOT}/app/design/frontend/${DEPLOY_THEME}/web/tailwind" run build \
  || die "CSS build failed"

# Step 5: CRITICAL: delete preprocessed and static files BEFORE deploy
log "[5/7] Delete static files"
rm -rf "${MAGENTO_ROOT}/var/view_preprocessed/"*
rm -rf "${MAGENTO_ROOT}/pub/static/frontend/"*

# Step 6: Static content deploy
log "[6/7] Static content deploy"
bin/magento setup:static-content:deploy "$DEPLOY_LOCALE" \
  -t "$DEPLOY_THEME" -f || die "static-content:deploy failed"

# Step 7: Flush all caches
log "[7/7] Cache flush"
bin/magento cache:flush || die "cache:flush failed"

log "=== Deploy completed successfully ==="

3. Using bin/magento safely in shell scripts

Integrating bin/magento into Magento maintenance scripts comes with a few important rules. First, never call php bin/magento directly: the bin/magento wrapper sets up environment variables, container context and PHP CLI configuration correctly. In Docker environments (Mark Shust setup), this means bin/magento runs the command inside the PHP container, while a direct php bin/magento call would use the local PHP interpreter, which is either the wrong version or does not exist at all. Second, always check the exit codes of bin/magento commands: many Magento commands print warnings to stdout but still return exit code 0 even when something went wrong.

The pattern for robust bin/magento integration in Magento maintenance scripts is: enable maintenance mode with bin/magento maintenance:enable before critical commands, run the command, then disable the mode again, all inside a trap-guarded block that also disables maintenance mode if something fails. This prevents a failed deployment from leaving the site stuck in maintenance mode indefinitely. For long-running commands such as setup:static-content:deploy, a timeout wrapper is worth adding: timeout 1800 bin/magento setup:static-content:deploy aborts after 30 minutes.

4. Database and file backup with rsync and mysqldump

A complete Magento backup in Bash consists of two components: a database dump and a file backup. For the database dump, bin/mysqldump (in the Docker wrapper context) or a direct mysqldump call with the correct parameters is the tool of choice. What matters for Magento: --single-transaction for a consistent dump without locking, and --quick for streaming large tables. Compressing the dump directly through a pipe (mysqldump ... | gzip -9 > backup.sql.gz) saves disk I/O and typically cuts the backup size by a factor of 5 to 10.

The file backup with rsync in Magento maintenance scripts follows a defined exclusion pattern. Not needed: var/cache/, var/page_cache/, var/session/, pub/static/ (redeployed anyway), vendor/ (reproducible from Composer). Needed: app/code/, app/design/, app/etc/ (config), pub/media/ (product images), var/log/ (optional). Running rsync --delete --checksum in incremental mode against a backup directory produces efficient backups that only transfer changed files.


#!/usr/bin/env bash
# magento-backup.sh: database and file backup for Magento 2
set -euo pipefail

readonly MAGENTO_ROOT="${MAGENTO_ROOT:-./src}"
readonly BACKUP_DIR="${BACKUP_DIR:-/var/backups/magento}"
readonly BACKUP_DATE=$(date +%Y%m%d-%H%M%S)
readonly KEEP_DAYS="${KEEP_DAYS:-14}"

log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; }

mkdir -p "${BACKUP_DIR}/db" "${BACKUP_DIR}/files"

# Database backup: single transaction for consistency, piped to gzip
log "Starting database backup..."
DB_FILE="${BACKUP_DIR}/db/magento-${BACKUP_DATE}.sql.gz"

bin/mysqldump \
  --single-transaction \
  --quick \
  --routines \
  --triggers \
  magento 2>/dev/null \
  | gzip -9 > "$DB_FILE"

DB_SIZE=$(du -sh "$DB_FILE" | cut -f1)
log "Database backup: $DB_FILE ($DB_SIZE)"

# File backup with rsync: exclude reproducible/cache directories
log "Starting file backup..."
FILES_DIR="${BACKUP_DIR}/files/${BACKUP_DATE}"
mkdir -p "$FILES_DIR"

rsync -a --checksum --delete \
  --exclude='var/cache/' \
  --exclude='var/page_cache/' \
  --exclude='var/session/' \
  --exclude='var/view_preprocessed/' \
  --exclude='pub/static/' \
  --exclude='vendor/' \
  --exclude='.git/' \
  "${MAGENTO_ROOT}/" "${FILES_DIR}/"

FILES_SIZE=$(du -sh "$FILES_DIR" | cut -f1)
log "File backup: $FILES_DIR ($FILES_SIZE)"

# Cleanup old backups
log "Cleaning backups older than ${KEEP_DAYS} days..."
find "${BACKUP_DIR}/db" -name "*.sql.gz" -mtime +"$KEEP_DAYS" -delete
find "${BACKUP_DIR}/files" -maxdepth 1 -type d -mtime +"$KEEP_DAYS" \
  -exec rm -rf {} + 2>/dev/null || true

log "Backup completed successfully"

5. Cache management: flush, warm up and selective invalidation

Magento cache management in Bash maintenance scripts goes well beyond a simple bin/magento cache:flush. Full cache management includes: selective cache type invalidation (clearing only certain cache types), checking cache status, and warming up specific pages after a flush to reduce first-request overhead. The pattern bin/magento cache:clean config full_page clears only the config and page cache without touching the block cache or generated cache, which is enough after configuration changes and considerably faster than a full flush.

For cache warm-up after a deploy, a simple Bash maintenance script using curl and a list of URLs is the most common pattern. The script fetches the most important pages (homepage, category pages, best-selling product pages) over HTTP, priming the Varnish or Magento page cache. Using curl -s -o /dev/null -w "%{http_code} %{time_total}s %{url_effective}\n" gives you the status code and load time in one go. Running the warm-up in parallel with & and job control cuts the warm-up time down to a fraction of the sequential approach.

6. Static content deploy: optimization and error handling

The setup:static-content:deploy step is typically the most time-consuming part of a Magento deployment. In Magento maintenance scripts it can be sped up considerably through parallel processing: bin/magento setup:static-content:deploy --jobs 4 uses 4 parallel workers. For multi-locale shops with several languages and themes, parallelization is critical; without it this step can take 10 to 20 minutes. Another trick: --strategy compact deploys shared assets only once instead of copying them for every theme and locale.

Error handling for the static content deploy in Magento maintenance scripts matters because this step can fail on missing dependencies or layout XML errors without returning a non-zero exit code. The pattern: after the deploy, check whether the expected CSS files actually exist. test -f "${MAGENTO_ROOT}/pub/static/frontend/${DEPLOY_THEME}/${DEPLOY_LOCALE}/css/styles-m.css" verifies that the deploy succeeded. If the file is missing, the script fails with a clear error before the cache gets flushed.

7. Log rotation for Magento logs

Magento writes a large number of log files into var/log/: system.log, exception.log, debug.log, magento.log and many module-specific logs. Without Magento log rotation in Bash these files grow without limit; multi-gigabyte exception.log files are not unusual in unmaintained shops. The standard tool logrotate with a Magento-specific configuration is the simplest solution. On top of that, a Magento maintenance script for daily log analysis is worthwhile: how many exceptions occurred yesterday? Are there new exception types?

The Bash pattern for manual log rotation as an alternative to logrotate: rename the current log files with a date stamp (mv system.log "system.$(date +%Y%m%d).log"), compress them (gzip), create a fresh empty file, and let the Magento process keep writing without a restart. For Magento running in Docker containers, logrotate inside the container, or a volume-mounted logrotate configuration on the host, is the cleaner solution. The Magento maintenance script also adds automatic evaluation on top of rotation: new exceptions since the last run get extracted and recorded in the monitoring log.


#!/usr/bin/env bash
# magento-log-maintenance.sh: log rotation and analysis for Magento 2
set -euo pipefail

readonly MAGENTO_ROOT="${MAGENTO_ROOT:-./src}"
readonly LOG_DIR="${MAGENTO_ROOT}/var/log"
readonly ARCHIVE_DIR="${LOG_DIR}/archive"
readonly MAX_SIZE_MB="${MAX_SIZE_MB:-100}"
readonly KEEP_DAYS="${KEEP_DAYS:-30}"

log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; }

mkdir -p "$ARCHIVE_DIR"

# Rotate oversized Magento logs
for logfile in "${LOG_DIR}"/*.log; do
  [[ -f "$logfile" ]] || continue

  size_mb=$(( $(stat -c%s "$logfile") / 1024 / 1024 ))
  filename=$(basename "$logfile")

  if (( size_mb > MAX_SIZE_MB )); then
    archive="${ARCHIVE_DIR}/${filename%.log}.$(date +%Y%m%d-%H%M%S).log.gz"
    log "Rotating $filename (${size_mb}MB) -> $archive"

    # Atomic rotate: copy, compress, truncate (don't delete, PHP keeps fd open)
    cp "$logfile" "${logfile}.rotating"
    gzip -9 "${logfile}.rotating"
    mv "${logfile}.rotating.gz" "$archive"
    # Truncate instead of delete: active PHP process keeps file descriptor
    : > "$logfile"
  fi
done

# Analyze exceptions from last 24 hours
echo ""
echo "=== Exception Summary (last 24h) ==="
SINCE=$(date -d '-24 hours' '+%Y-%m-%d %H:%M:%S')
if [[ -f "${LOG_DIR}/exception.log" ]]; then
  grep "^\[$(date +%Y)" "${LOG_DIR}/exception.log" \
    | awk -v since="$SINCE" '$0 >= "[" since { print }' \
    | grep -oP "(?<=: )[A-Za-z\\\\]+Exception[^\\n]*" \
    | sort | uniq -c | sort -rn | head -10 \
    || echo "No exceptions in last 24h"
fi

# Clean archived logs older than KEEP_DAYS
find "$ARCHIVE_DIR" -name "*.log.gz" -mtime +"$KEEP_DAYS" -delete
log "Cleanup done: removed archives older than ${KEEP_DAYS} days"

8. Setting and lifting maintenance mode safely

Magento's maintenance mode is essential during deployments so that customers never see a partially updated site. In Magento maintenance scripts in Bash, enabling and disabling maintenance mode must always follow a trap-guarded pattern: the trap handler registers bin/magento maintenance:disable for the EXIT signal, so the mode gets disabled automatically even if the script fails. Without this trap, a failed deploy script risks leaving the site stuck in maintenance mode permanently.

For zero-downtime deployments with IP whitelisting, Magento offers the option to exempt certain IPs from maintenance mode. The Magento maintenance script enables maintenance mode with whitelisting for the staging IP (bin/magento maintenance:enable --ip=STAGING_IP), runs the deployment, and then disables the mode again. This lets you verify the result directly in the browser before all users see the new version. The script also logs the maintenance windows to a log file for later review.

9. Magento maintenance tasks compared

The various Magento maintenance tasks in Bash differ in how often they run, how risky they are, and how far they can be automated. The overview below helps prioritize which scripts to build first.

Task Frequency Risk without a script Key pattern
Deploy On releases Forgotten steps, cache inconsistency set -e, fixed sequence, trap for maintenance mode
Database backup Daily Data loss on crash --single-transaction | gzip, retention policy
Cache flush After changes Stale content visible to customers Selective: cache:clean instead of cache:flush
Log rotation Daily Disk full, PHP errors from full disk Truncate instead of delete, dated archives
File backup Daily/weekly Product images and code not recoverable rsync --delete --checksum with an exclusion list

Deploy scripts and database backups carry the highest priority, since errors there have a direct impact on production. Log rotation and file backup are routine maintenance tasks with a lower urgency in the short term, but they remain just as critical over the long run. Cache management scripts solve concrete customer-facing problems after deployments and configuration changes.

Mironsoft

Magento 2 development, automation and production infrastructure

Want to fully automate your Magento deployments and maintenance?

We build robust Magento maintenance scripts in Bash for your shop: fully automated deploy sequences, daily backups with retention, selective cache management and log rotation, all with complete error handling and monitoring integration.

Deploy automation

Zero-downtime deploy scripts with maintenance mode, rollback and verification

Backup & recovery

Daily database and file backups with a retention policy and recovery tests

Monitoring & logs

Log rotation, exception reports and alerting for Magento production environments

10. Summary

Magento maintenance scripts in Bash make the difference between error-prone manual deployments and reliable automation. A deploy sequence built as a Bash script with set -euo pipefail and an explicit step order prevents the most common deploy mistakes: forgotten static file deletion, missing DI compilation and an inconsistent cache state after deployment. bin/magento should never be called directly through php bin/magento, always through the wrapper that guarantees the correct execution context.

Daily Magento maintenance scripts for backup, log rotation and cache analysis protect the production environment over the long term: database backups with --single-transaction and piped compression, rsync file backups with a sensible exclusion list, log rotation through truncating instead of deleting for running PHP processes, and automatic exception reports for proactive monitoring. Together, these scripts form a complete Magento operations automation setup.

Magento Maintenance Scripts in Bash: The Essentials at a Glance

Deploy sequence

Composer, setup:upgrade, di:compile, CSS build, delete static files, SCD, cache:flush. The order never changes. set -e stops on any failure.

bin/magento wrapper

Never call php bin/magento directly. The wrapper sets the container context and PHP CLI config. Always guard maintenance mode with trap EXIT.

Backup

mysqldump --single-transaction | gzip -9 for consistent, compressed database dumps. rsync --delete --checksum excluding cache/, static/ and vendor/.

Log rotation

Truncate instead of delete: running PHP processes keep the file descriptor open. Archive with a date stamp, compress, and apply a retention policy for old archives.

11. FAQ: Magento Maintenance Scripts in Bash

1Why not call php bin/magento directly?
The bin/magento wrapper sets the container context, PHP CLI config and permissions. A direct php bin/magento call uses the wrong PHP interpreter or fails outright in a Docker context.
2Delete static files before SCD?
Without deleting them, old CSS/JS stays active. rm -rf var/view_preprocessed/* pub/static/frontend/* is a mandatory step before setup:static-content:deploy.
3Consistent MySQL dump without locking?
mysqldump --single-transaction: a consistent InnoDB transaction without table locks. --quick for streaming on large tables.
4Maintenance mode stuck after a failure?
trap 'bin/magento maintenance:disable' EXIT disables the mode on every exit, including set -e aborts and signals.
5Which directories to exclude from backups?
Exclude: var/cache/, pub/static/, vendor/. Include: app/code/, app/design/, app/etc/, pub/media/ (product images!).
6Rotate logs without interrupting PHP?
: > var/log/system.log truncates the file without closing the file descriptor. PHP keeps writing to the same inode. Copy and archive first, then truncate.
7Speed up static content deploy?
--jobs 4 for parallel workers. --strategy compact for shared assets. Deploy only the locales and themes you actually need.
8Verify a successful deploy?
test -f pub/static/frontend/Theme/Locale/css/styles-m.css checks the SCD result. A curl request to the homepage with an HTTP status check after cache:flush.
9How often to create database backups?
Daily, plus before every deployment. At least 14 days of retention. Transfer backups to separate storage: local backups alone are not enough for disaster recovery.
10Cache warm-up after a deploy?
Parallel curl loop: for url in $urls; do curl -s -o /dev/null "$url" & done; wait. Prime the homepage, top categories and best-selling products.