Nantes-Toulouse en avion (ou presque)

Comme l’oral du CAPES se déroule cette année encore à Toulouse, j’ai choisi le moyen de transport le plus court et le moins fatiguant : l’avion. En effet, le trajet Nantes/Toulouse dure 1 heure tout rond alors que cela prend environ 6 heures en voiture ou en train. Et vu qu’il faisait entre 36° et 39°, le choix fut vite fait, surtout que l’avion revient à peu près au même prix que la voiture avec les péages – si l’on a moins de 25 ans bien sûr.

Mais un voyage se passe-t-il vraiment sans péripéties ? Le mot voyage ne dénote-t-il pas la notion d’aventure, les petits plus qui font qu’il restera gravé à jamais dans nos mémoires ? Assurément. Et d’ailleurs en voici un petit aperçu.

Mercredi 29 juin – 14h. Arrivée à l’aéroport de Nantes Atlantique. Enregistrement du sac de voyage et direction le hall d’embarquement. J’attends depuis déjà une bonne demi-heure lorsque j’aperçois un regroupement de personnes près du comptoir. Je lève alors les yeux vers l’écran de contrôle, sur lequel je lis « Vol pour Toulouse : retard 25 min ». Ah. Je me rapproche de l’hôtesse et tend l’oreille : l’avion est bien arrivé mais il est dans l’incapacité technique de redécoller, plus d’informations nous seront communiquées dans les minutes suivantes. Bon, cela commence… Effectivement, la nouvelle tombe sans trop tarder : le vol est tout bonnement annulé. Comme ça, paf ! Le jour où je dois me rendre à Toulouse pour passer mon concours. Il est alors 15h50 et je dois être impérativement à Toulouse le jour suivant à 14h…

Je me dirige donc comme tout le monde vers le comptoir réclamations, ouvert spécialement pour nous. Première étape : récupérer le sac sur le tapis. Seconde étape : écoute des options de réacheminement. Ce sont en fait des options qui n’en sont pas – jugez plutôt : les billets « affaires » ont été automatiquement reporté sur le vol suivant. Les autres ont eu le droit à un Nantes/Paris suivi d’un Paris/Toulouse. Autant dire que l’ambiance était électrique – appréciez la litote ! Bien évidemment, nous eûmes droit à une seconde surprise : le vol Nantes/Paris a accusé un retard de 30 minutes – ce qui amputait sérieusement mes chances d’attraper la correspondance et pouvait m’obliger à rester bloqué à Paris. Course effrénée dans le terminal 2F, arrivée à la porte 35 en nage, petit coup d’oeil circulaire inquiet : c’est bon, l’embarquement n’a pas commencé. Et pour cause, le dernier vol accuse lui aussi un retard de 15 minutes. Décidément, à Air France de nous faire préférer le train !

23h50. Arrivé enfin à Toulouse, j’attrape mon sac et me jette dans la navette qui me dépose devant la gare Matabiau. Un petit kilomètre plus loin, haletant et toujours en nage, j’aperçois la lumière bleutée de l’enseigne de l’Etap Hôtel, tel un phare dans la nuit. Je m’engouffre et tombe sur le veilleur de nuit qui m’alloue la chambre 700 et un numéro digicode impossible à retenir. Je monte au 7ème étage, tape le code : rien. Voyant rouge qui refuse de passer au vert. Je réessaie. Toujours rien. Maudissant cette journée infernale, je redescends au rez-de-chaussée et informe le veilleur que son code ne fonctionne pas. Extraits.

Veilleur  » Le code ne marche pas ? Vous êtes sûrs d’avoir tapé le bon code et d’avoir appuyé assez fort sur les touches » ?
Matt (« Mais tu me prends vraiment pour une bille mon garçon ?!? ») Oui, j’ai même tenté à plusieurs reprises.
Veilleur « Bon on va voir ça ensemble ».

Nous remontons. Nous testons. Et nous constatons que le code ne fonctionne pas. Il m’ouvre avec son passe et m’annonce aussi fier que s’il avait inventé l’eau chaude :

Veilleur : « Ben voilà, maintenant vous y êtes ! Si vous voulez sortir, vous fermez la porte et si vous voulez rentrer ben vous venez me voir et je vous ouvrirai ».
Matt :  » Hein ?! Je vais devoir vous chercher à chaque fois que je veux entrer dans ma chambre ?! Mais c’est pas possible ça ! Je ne peux pas être relogé ? »
Veilleur : « Ah ben non, l’hôtel est complet ».
Matt : « Trop cool. Bonne nuit ».

Jeudi 30 juin – Minuit 30. Fin du périple. Je viens de prendre ma douche et je suis claqué. Mon voyage qui devait prendre trois heures en a pris au final plus de dix. Oui, je crois qu’on peut dire que j’aime l’aventure…

L'écran d'un ordinateur portable affiche un logiciel de montage vidéo avec l'image d'un disque vinyle et le texte "FLAC Splitter". Au premier plan, une grande icône de fichier FLAC apparaît, mettant en évidence les outils permettant de couper un fichier APE ou d'utiliser un fichier CUE.

Couper un fichier APE avec un fichier CUE

Vous avez un album entier dans un seul fichier .ape, accompagné d’un fichier .cue, et vous voulez obtenir une piste par morceau ?

C’est exactement le rôle du fichier CUE.

Le fichier .ape contient l’audio complet de l’album. Le fichier .cue contient la table des pistes : titres, artistes, index, durée, ordre des morceaux et parfois informations de CD audio.

Autrefois, on passait par Winamp, EAC, un plugin de sortie WAV et un outil comme Medieval CUE Splitter. Cette méthode fonctionnait, mais elle est datée. Aujourd’hui, il vaut mieux utiliser CUETools, foobar2000 ou une ligne de commande propre avec shntool.

Lire la suite

Backup files and databases easily with cron photo

Backup files and databases automatically with cron

A good backup is not a command you copied from a forum in 2005.

A good backup is automated, readable, logged, rotated, stored away from the server, and tested before disaster strikes. Cron can still handle the scheduling part perfectly well. However, the backup command itself needs a little more care than a one-liner thrown into cPanel.

This guide shows a modern way to back up website files and MySQL or MariaDB databases with cron. It works well for small and medium websites, WordPress installs, forums, static sites and custom PHP applications.

If you run a large database, a busy WooCommerce shop or a high-write application, treat this as a baseline. You may need replica-based backups, physical backups, binary logs, snapshots or managed backup tooling.

What should a backup include?

For most websites, you need two things:

  • the website files, such as themes, plugins, uploads, media and custom code;
  • the database, which contains posts, pages, users, settings, orders, comments and application data.

Backing up only the files is not enough. Backing up only the database is not enough either. A WordPress site without its database is a cupboard with no shelves. A database without uploads is a library with missing pages.

The old one-liner approach

The old version of this tutorial used two simple cron commands: one for tar, one for mysqldump.

date=`date -I`; tar -zcf backup_$date.tgz ./public_htmlLangage du code : JavaScript (javascript)
date=`date -I`; mysqldump -uDBUSER -pDBPASS --all-databases | gzip > /home/CPANEL/xbackup_$date.sql.gzLangage du code : JavaScript (javascript)

Those commands are useful for learning the concept. They are not ideal for production.

  • The database password appears directly in the cron command.
  • The backup stays on the same server.
  • There is no retention policy.
  • There is no log file.
  • There is no restore test.
  • The files archive may include cache directories and previous backups.
  • The command becomes hard to maintain as the site grows.

So, let’s keep the simplicity of cron, but move the logic into a small shell script.

Create a backup directory

First, create a directory for local temporary backups. This directory should not be publicly accessible from the web.

mkdir -p "$HOME/backups"
chmod 700 "$HOME/backups"Langage du code : JavaScript (javascript)

Do not store backups inside public_html, htdocs, www or any public web root. A database dump exposed on the web is not a backup. It is a gift basket for attackers.

Store database credentials safely

Avoid putting database passwords directly in cron. Instead, create a protected MySQL option file.

nano "$HOME/.my.cnf"Langage du code : JavaScript (javascript)

Add the credentials for a dedicated backup user:

[client]
user=backup_user
password=replace_with_a_long_password
host=localhost

Then lock down the file permissions:

chmod 600 "$HOME/.my.cnf"Langage du code : JavaScript (javascript)

The backup user should only have the privileges it needs. For many simple sites, read-oriented privileges are enough. If you dump stored routines, triggers or events, you may need additional privileges depending on your MySQL or MariaDB version and configuration.

Use mysqldump or mariadb-dump?

Use the dump tool that matches your database server.

Database serverRecommended commandNotes
MySQLmysqldumpOfficial MySQL logical backup utility.
MariaDBmariadb-dumpModern MariaDB name. mysqldump may still exist as a compatibility alias on some systems.

For a small WordPress site on MySQL, mysqldump is still fine. For MariaDB, prefer mariadb-dump when available.

Check what your server provides:

command -v mysqldump
command -v mariadb-dump

A modern cron backup script

Create a script outside the public web root:

mkdir -p "$HOME/bin"
nano "$HOME/bin/site-backup.sh"Langage du code : JavaScript (javascript)

Paste this version and adjust the paths at the top.

#!/usr/bin/env bash

set -Eeuo pipefail

SITE_NAME="example-site"
WEB_ROOT="$HOME/public_html"
BACKUP_ROOT="$HOME/backups"
RETENTION_DAYS="14"

DATE="$(date +%F_%H-%M-%S)"
BACKUP_DIR="${BACKUP_ROOT}/${SITE_NAME}_${DATE}"
LOG_FILE="${BACKUP_ROOT}/${SITE_NAME}_${DATE}.log"

DB_DUMP_COMMAND="mysqldump"
DB_NAME="wordpress_database"

mkdir -p "$BACKUP_DIR"

log() {
	printf '[%s] %s\n' "$(date '+%F %T')" "$*" | tee -a "$LOG_FILE"
}

cleanup_failed_backup() {
	log "Backup failed. Removing incomplete directory: ${BACKUP_DIR}"
	rm -rf "$BACKUP_DIR"
}

trap cleanup_failed_backup ERR

log "Starting backup for ${SITE_NAME}"

if ! command -v "$DB_DUMP_COMMAND" >/dev/null 2>&1; then
	log "Dump command not found: ${DB_DUMP_COMMAND}"
	exit 1
fi

if [ ! -d "$WEB_ROOT" ]; then
	log "Web root does not exist: ${WEB_ROOT}"
	exit 1
fi

log "Dumping database: ${DB_NAME}"

"$DB_DUMP_COMMAND" \
	--single-transaction \
	--quick \
	--routines \
	--triggers \
	--events \
	"$DB_NAME" | gzip -9 > "${BACKUP_DIR}/${DB_NAME}.sql.gz"

log "Archiving website files"

tar \
	--create \
	--gzip \
	--file "${BACKUP_DIR}/${SITE_NAME}_files.tar.gz" \
	--directory "$WEB_ROOT" \
	--exclude='./cache' \
	--exclude='./wp-content/cache' \
	--exclude='./wp-content/uploads/cache' \
	--exclude='./wp-content/upgrade' \
	--exclude='./wp-content/debug.log' \
	.

log "Creating checksum file"

(
	cd "$BACKUP_DIR"
	sha256sum ./* > SHA256SUMS
)

log "Removing local backups older than ${RETENTION_DAYS} days"

find "$BACKUP_ROOT" \
	-mindepth 1 \
	-maxdepth 1 \
	-type d \
	-name "${SITE_NAME}_*" \
	-mtime "+${RETENTION_DAYS}" \
	-exec rm -rf {} +

find "$BACKUP_ROOT" \
	-mindepth 1 \
	-maxdepth 1 \
	-type f \
	-name "${SITE_NAME}_*.log" \
	-mtime "+${RETENTION_DAYS}" \
	-delete

log "Backup completed successfully: ${BACKUP_DIR}"

trap - ERRLangage du code : PHP (php)

Make the script executable:

chmod 700 "$HOME/bin/site-backup.sh"Langage du code : JavaScript (javascript)

Then run it manually before adding it to cron:

"$HOME/bin/site-backup.sh"Langage du code : JSON / JSON avec commentaires (json)

If the manual run fails, fix that first. Cron will not magically make a broken command better. Cron is punctual, not merciful.

For MariaDB, switch the dump command

If your server runs MariaDB and provides mariadb-dump, change this line:

DB_DUMP_COMMAND="mysqldump"Langage du code : JavaScript (javascript)

to:

DB_DUMP_COMMAND="mariadb-dump"Langage du code : JavaScript (javascript)

If you later restore the dump on a different system, test compatibility first. Recent MariaDB dump files may include MariaDB-specific directives that older clients or MySQL clients do not understand.

Back up all databases or one database?

The old tutorial used --all-databases. That can still make sense on a small VPS where you control every database.

For shared hosting or a single website, backing up only the relevant database is cleaner:

mysqldump --single-transaction --quick --routines --triggers --events wordpress_database | gzip -9 > wordpress_database.sql.gz

For all databases on a server you administer:

mysqldump --all-databases --single-transaction --quick --routines --triggers --events | gzip -9 > all-databases.sql.gz

The --single-transaction option is especially useful with InnoDB tables because it creates a consistent snapshot without locking normal reads and writes for the full duration of the dump.

Exclude cache and temporary files

Do not back up everything blindly. Cache folders can make backups huge, slow and noisy.

For WordPress, common exclusions include:

  • wp-content/cache
  • wp-content/upgrade
  • wp-content/uploads/cache
  • wp-content/debug.log
  • plugin-specific cache directories
  • previous backup archives

GNU tar supports exclusion patterns with --exclude and exclusion files with --exclude-from. For a larger setup, move exclusions into a dedicated file.

nano "$HOME/backup-excludes.txt"Langage du code : JavaScript (javascript)
./wp-content/cache
./wp-content/upgrade
./wp-content/uploads/cache
./wp-content/debug.log
./backup
./backups

Then call tar with:

tar --create --gzip --file files.tar.gz --directory "$WEB_ROOT" --exclude-from="$HOME/backup-excludes.txt" .Langage du code : JavaScript (javascript)

Add the script to cron

Open your crontab:

crontab -e

Run the backup every night at 02:30:

30 2 * * * /home/your-user/bin/site-backup.sh >> /home/your-user/backups/cron.log 2>&1Langage du code : JavaScript (javascript)

Use absolute paths in cron. Your interactive shell and cron do not always share the same environment, PATH or working directory.

On cPanel, use the Cron Jobs interface and paste the same command. Adjust the path to match your hosting account:

/home/cpanel-user/bin/site-backup.sh >> /home/cpanel-user/backups/cron.log 2>&1Langage du code : JavaScript (javascript)

Do not keep backups only on the same server

A local backup is useful for quick restores. It is not enough.

If the disk fails, the account is deleted, the server is compromised or the hosting provider has an incident, local backups can disappear with the original data.

At minimum, copy backups to another location:

  • another server via rsync over SSH;
  • object storage such as S3-compatible storage;
  • a backup server provided by your host;
  • a dedicated backup tool such as BorgBackup, Restic or Rclone.

For a simple remote copy with rsync:

rsync -az --delete "$HOME/backups/" backup-user@backup.example.com:/srv/backups/example-site/Langage du code : JavaScript (javascript)

Add this only after SSH keys and permissions are correctly configured. The remote backup user should have limited access to the backup directory, not full access to the server.

Check that backups can be restored

A backup you never restore is only a hopeful archive.

Test the database dump regularly on a staging machine or local environment:

gunzip -c wordpress_database.sql.gz | mysql staging_database

Test the file archive too:

tar -tzf example-site_files.tar.gz | head

Verify checksums:

cd /path/to/backup-directory
sha256sum -c SHA256SUMS

For WordPress, a real restore test means checking that the site loads, the admin works, uploads are present, permalinks work and key plugin data survived the import.

Recommended backup frequency

The right schedule depends on how often your data changes.

Site typeDatabase backupFiles backupComment
Static or brochure siteWeeklyWeeklyIncrease before updates.
Blog with regular postsDailyDaily or weeklyUploads matter when publishing.
Forum or membership siteDaily or moreDailyUser activity changes often.
WooCommerce shopHourly or managed backupsDailyOrders make daily-only backups risky.
Development siteBefore major changesBefore major changesAutomate if clients edit content.

For WooCommerce, daily backups can lose orders. Use your host’s continuous backups, database binlogs, a transactional backup service, or a custom strategy that matches your recovery point objective.

Cron or systemd timers?

Cron is still perfectly fine for simple scheduled backups. It is available almost everywhere, including shared hosting and cPanel.

On a modern VPS, systemd timers can be cleaner. They integrate with journalctl, handle missed runs better and offer more explicit service definitions. Still, cron wins when portability matters.

Use cron when you want the simplest path. Use systemd timers when you manage the whole server and want stronger observability.

Security checklist

  • Store backups outside the public web root.
  • Use chmod 600 for database credential files.
  • Use a dedicated database backup user.
  • Do not put passwords directly in cron.
  • Compress dumps, but do not rely on compression as security.
  • Encrypt off-server backups if they contain personal data.
  • Limit remote backup SSH keys.
  • Rotate old backups automatically.
  • Monitor failed cron runs.
  • Test restores regularly.

Quick troubleshooting

If the script works manually but not in cron, check these first:

  • use absolute paths everywhere;
  • check file permissions;
  • redirect cron output to a log file;
  • verify that mysqldump or mariadb-dump is in cron’s PATH;
  • check that $HOME resolves as expected;
  • confirm the backup user can read the database;
  • check available disk space before compression.

Useful commands:

df -h
du -sh "$HOME/backups"
tail -100 "$HOME/backups/cron.log"
command -v mysqldump
command -v mariadb-dumpLangage du code : JavaScript (javascript)

Conclusion

Cron remains a solid way to automate website backups. The mistake is not cron. The mistake is treating a backup as a one-line afterthought.

Put the logic in a script, keep credentials out of the crontab, dump the database with consistent options, exclude cache files, rotate old archives, copy backups off-server and test restoration. That gives you a backup process you can actually trust when something breaks.

Because the only backup that matters is the one that restores cleanly. Everything else is decorative gzip.

Sources and documentation