<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Self-Hosting on /dev/random</title>
    <link>https://log.2027a.net/tags/self-hosting/</link>
    <description>Recent content in Self-Hosting on /dev/random</description>
    <image>
      <title>/dev/random</title>
      <url>https://log.2027a.net/img/cover.jpg</url>
      <link>https://log.2027a.net/img/cover.jpg</link>
    </image>
    <generator>Hugo -- 0.149.1</generator>
    <language>fr</language>
    <lastBuildDate>Sat, 22 Mar 2025 09:03:28 -0400</lastBuildDate>
    <atom:link href="https://log.2027a.net/tags/self-hosting/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Sauvegardes avec rsync</title>
      <link>https://log.2027a.net/posts/sauvegardes-avec-rsync/</link>
      <pubDate>Sat, 22 Mar 2025 09:03:28 -0400</pubDate>
      <guid>https://log.2027a.net/posts/sauvegardes-avec-rsync/</guid>
      <description>&lt;p&gt;J&amp;rsquo;ai récement eu quelques petits problèmes avec un serveur (une sombre histoire de
RAM corrompue), et ça a été l&amp;rsquo;occasion de peaufiner un &lt;em&gt;helper script&lt;/em&gt; pour &lt;code&gt;rsync&lt;/code&gt;
que j&amp;rsquo;utilise depuis un moment.&lt;/p&gt;
&lt;p&gt;Ce script est loin d&amp;rsquo;être parfait (en particulier, il ne vérifie pas le contenu des
source &amp;amp; exclude files), mais c&amp;rsquo;est une bonne base pour un système de sauvegarde
robuste.&lt;/p&gt;
&lt;p&gt;Ici, je m&amp;rsquo;en sers en conjonction avec &lt;a href=&#34;https://docs.duplicati.com/&#34;&gt;duplicati&lt;/a&gt; pour
une sauvegarde automatique du contenu de mes serveurs et de mon laptop, mais vous
pourriez l&amp;rsquo;utiliser autrement. Vous le trouverez
&lt;a href=&#34;https://github.com/isingasimplesong/duct-tape/blob/main/rsync_backup.sh&#34;&gt;dans mon repo &lt;em&gt;duct-tape&lt;/em&gt; sur github&lt;/a&gt;&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>J&rsquo;ai récement eu quelques petits problèmes avec un serveur (une sombre histoire de
RAM corrompue), et ça a été l&rsquo;occasion de peaufiner un <em>helper script</em> pour <code>rsync</code>
que j&rsquo;utilise depuis un moment.</p>
<p>Ce script est loin d&rsquo;être parfait (en particulier, il ne vérifie pas le contenu des
source &amp; exclude files), mais c&rsquo;est une bonne base pour un système de sauvegarde
robuste.</p>
<p>Ici, je m&rsquo;en sers en conjonction avec <a href="https://docs.duplicati.com/">duplicati</a> pour
une sauvegarde automatique du contenu de mes serveurs et de mon laptop, mais vous
pourriez l&rsquo;utiliser autrement. Vous le trouverez
<a href="https://github.com/isingasimplesong/duct-tape/blob/main/rsync_backup.sh">dans mon repo <em>duct-tape</em> sur github</a></p>
<h2 id="le-fichier-de-configuration">Le fichier de configuration</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-txt" data-lang="txt"><span class="line"><span class="cl">BACKUP_MOUNT_POINT=&#34;/path/to/mountpoint&#34;
</span></span><span class="line"><span class="cl">BACKUP_DESTINATION=&#34;/path/to/mointpoint/and/final/destination&#34;
</span></span><span class="line"><span class="cl">BACKUP_SOURCE_FILE=&#34;/home/user/.config/rsync_source.list&#34;
</span></span><span class="line"><span class="cl">EXCLUDE_FILE=&#34;/home/user/.config/rsync_exclude.list&#34;
</span></span><span class="line"><span class="cl">BACKUP_LOG_FILE=&#34;/home/user/.local/logs/rsync_backup.log&#34;
</span></span><span class="line"><span class="cl">LOGROTATE_MAX_SIZE=2  # en Mo
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># Notifications (facultatif)
</span></span><span class="line"><span class="cl">PUSHOVER_API_TOKEN=&#34;xxx-xxx-xxx&#34;
</span></span><span class="line"><span class="cl">PUSHOVER_USER_KEY=&#34;xxx-xxx-xxx&#34;
</span></span><span class="line"><span class="cl">NOTIFY_TITLE=&#34;Rsync Backup&#34;
</span></span></code></pre></div><h2 id="le-script">Le script</h2>
<p><code>rsync_backup.sh</code> :</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="cp">#!/usr/bin/env bash
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1"># Rsync backup script with dry-run, log rotation, locking, and pushover notification (on failure only)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">set</span> -euo pipefail
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">CONF</span><span class="o">=</span><span class="s2">&#34;</span><span class="si">${</span><span class="nv">RSYNC_CONF</span><span class="k">:-</span><span class="nv">$HOME</span><span class="p">/.config/rsync_backup.conf</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">LOCKFILE</span><span class="o">=</span><span class="s2">&#34;/tmp/rsync_backup.lock&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">DRY_RUN</span><span class="o">=</span><span class="m">0</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">usage<span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    cat <span class="s">&lt;&lt;EOF
</span></span></span><span class="line"><span class="cl"><span class="s">Usage: $0 [--dry-run] [--help]
</span></span></span><span class="line"><span class="cl"><span class="s">
</span></span></span><span class="line"><span class="cl"><span class="s">Options:
</span></span></span><span class="line"><span class="cl"><span class="s">  --dry-run        Simulate the rsync backup without modifying any files
</span></span></span><span class="line"><span class="cl"><span class="s">  --help, -h       Show this help message and exit
</span></span></span><span class="line"><span class="cl"><span class="s">
</span></span></span><span class="line"><span class="cl"><span class="s">Description:
</span></span></span><span class="line"><span class="cl"><span class="s">  This script performs an rsync-based backup based on paths listed in a source file.
</span></span></span><span class="line"><span class="cl"><span class="s">  It supports log rotation, pushover notifications (only on failure), and ensures only one instance runs at a time.
</span></span></span><span class="line"><span class="cl"><span class="s">
</span></span></span><span class="line"><span class="cl"><span class="s">Configuration:
</span></span></span><span class="line"><span class="cl"><span class="s">  The script expects a configuration file exporting the following variables:
</span></span></span><span class="line"><span class="cl"><span class="s">
</span></span></span><span class="line"><span class="cl"><span class="s">    BACKUP_MOUNT_POINT     # mount point where backup destination is available
</span></span></span><span class="line"><span class="cl"><span class="s">    BACKUP_DESTINATION     # destination directory for rsync backup
</span></span></span><span class="line"><span class="cl"><span class="s">    BACKUP_SOURCE_FILE     # file listing source paths to back up (one per line)
</span></span></span><span class="line"><span class="cl"><span class="s">    EXCLUDE_FILE           # rsync exclude patterns (one per line)
</span></span></span><span class="line"><span class="cl"><span class="s">    BACKUP_LOG_FILE        # path to log file
</span></span></span><span class="line"><span class="cl"><span class="s">    LOGROTATE_MAX_SIZE     # max log file size in MB before rotation
</span></span></span><span class="line"><span class="cl"><span class="s">
</span></span></span><span class="line"><span class="cl"><span class="s">  Optional (for pushover notifications):
</span></span></span><span class="line"><span class="cl"><span class="s">
</span></span></span><span class="line"><span class="cl"><span class="s">    PUSHOVER_USER_KEY
</span></span></span><span class="line"><span class="cl"><span class="s">    PUSHOVER_API_TOKEN
</span></span></span><span class="line"><span class="cl"><span class="s">    NOTIFY_TITLE           # (optional) title shown in push notifications
</span></span></span><span class="line"><span class="cl"><span class="s">
</span></span></span><span class="line"><span class="cl"><span class="s">Default configuration file path: \$HOME/.config/rsync_backup.conf
</span></span></span><span class="line"><span class="cl"><span class="s">Example configuration file : https://log.2027a.net/posts/sauvegardes-avec-rsync/#le-fichier-de-configuration
</span></span></span><span class="line"><span class="cl"><span class="s">
</span></span></span><span class="line"><span class="cl"><span class="s">EOF</span>
</span></span><span class="line"><span class="cl">    <span class="nb">exit</span> <span class="m">0</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Parse arguments</span>
</span></span><span class="line"><span class="cl"><span class="k">while</span> <span class="o">[[</span> <span class="nv">$#</span> -gt <span class="m">0</span> <span class="o">]]</span><span class="p">;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="s2">&#34;</span><span class="nv">$1</span><span class="s2">&#34;</span> in
</span></span><span class="line"><span class="cl">    --dry-run<span class="o">)</span>
</span></span><span class="line"><span class="cl">        <span class="nv">DRY_RUN</span><span class="o">=</span><span class="m">1</span>
</span></span><span class="line"><span class="cl">        <span class="nb">shift</span>
</span></span><span class="line"><span class="cl">        <span class="p">;;</span>
</span></span><span class="line"><span class="cl">    --help <span class="p">|</span> -h<span class="o">)</span>
</span></span><span class="line"><span class="cl">        usage
</span></span><span class="line"><span class="cl">        <span class="p">;;</span>
</span></span><span class="line"><span class="cl">    *<span class="o">)</span>
</span></span><span class="line"><span class="cl">        <span class="nb">echo</span> <span class="s2">&#34;Unknown option: </span><span class="nv">$1</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">        usage
</span></span><span class="line"><span class="cl">        <span class="p">;;</span>
</span></span><span class="line"><span class="cl">    <span class="k">esac</span>
</span></span><span class="line"><span class="cl"><span class="k">done</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="o">[</span> ! -f <span class="s2">&#34;</span><span class="nv">$CONF</span><span class="s2">&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;Configuration file not found: </span><span class="nv">$CONF</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">source</span> <span class="s2">&#34;</span><span class="nv">$CONF</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Check required commands</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> cmd in rsync mountpoint curl gzip stat flock<span class="p">;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="nb">command</span> -v <span class="s2">&#34;</span><span class="nv">$cmd</span><span class="s2">&#34;</span> &gt;/dev/null <span class="o">||</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="nb">echo</span> <span class="s2">&#34;Missing command: </span><span class="nv">$cmd</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="k">done</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Check required vars</span>
</span></span><span class="line"><span class="cl"><span class="nv">required_vars</span><span class="o">=(</span>BACKUP_MOUNT_POINT BACKUP_DESTINATION BACKUP_SOURCE_FILE EXCLUDE_FILE BACKUP_LOG_FILE LOGROTATE_MAX_SIZE<span class="o">)</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> var in <span class="s2">&#34;</span><span class="si">${</span><span class="nv">required_vars</span><span class="p">[@]</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="o">[</span> -z <span class="s2">&#34;</span><span class="si">${</span><span class="p">!var</span><span class="k">:-</span><span class="si">}</span><span class="s2">&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">        <span class="nb">echo</span> <span class="s2">&#34;Missing required variable in config: </span><span class="nv">$var</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl">    <span class="k">fi</span>
</span></span><span class="line"><span class="cl"><span class="k">done</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Optional pushover</span>
</span></span><span class="line"><span class="cl"><span class="nv">HAS_PUSHOVER</span><span class="o">=</span><span class="m">0</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="o">[[</span> -n <span class="s2">&#34;</span><span class="si">${</span><span class="nv">PUSHOVER_USER_KEY</span><span class="k">:-</span><span class="si">}</span><span class="s2">&#34;</span> <span class="o">&amp;&amp;</span> -n <span class="s2">&#34;</span><span class="si">${</span><span class="nv">PUSHOVER_API_TOKEN</span><span class="k">:-</span><span class="si">}</span><span class="s2">&#34;</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="nv">HAS_PUSHOVER</span><span class="o">=</span><span class="m">1</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Functions</span>
</span></span><span class="line"><span class="cl">log<span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;</span><span class="k">$(</span>date <span class="s1">&#39;+%Y-%m-%d %H:%M:%S&#39;</span><span class="k">)</span><span class="s2"> - </span><span class="nv">$1</span><span class="s2"> - </span><span class="nv">$2</span><span class="s2">&#34;</span> &gt;&gt;<span class="s2">&#34;</span><span class="nv">$BACKUP_LOG_FILE</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">notify_pushover<span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="o">[</span> <span class="s2">&#34;</span><span class="nv">$HAS_PUSHOVER</span><span class="s2">&#34;</span> -eq <span class="m">1</span> <span class="o">]</span> <span class="o">||</span> <span class="k">return</span>
</span></span><span class="line"><span class="cl">    curl -s <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>        --form-string <span class="s2">&#34;token=</span><span class="nv">$PUSHOVER_API_TOKEN</span><span class="s2">&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>        --form-string <span class="s2">&#34;user=</span><span class="nv">$PUSHOVER_USER_KEY</span><span class="s2">&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>        --form-string <span class="s2">&#34;message=</span><span class="nv">$1</span><span class="s2">&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>        --form-string <span class="s2">&#34;title=</span><span class="si">${</span><span class="nv">NOTIFY_TITLE</span><span class="k">:-</span><span class="nv">rsync_backup</span><span class="si">}</span><span class="s2">&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>        https://api.pushover.net/1/messages.json &gt;/dev/null
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">log_rotate<span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="nb">local</span> <span class="nv">max_size</span><span class="o">=</span><span class="k">$((</span>LOGROTATE_MAX_SIZE <span class="o">*</span> <span class="m">1024</span> <span class="o">*</span> <span class="m">1024</span><span class="k">))</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="o">[</span> -f <span class="s2">&#34;</span><span class="nv">$BACKUP_LOG_FILE</span><span class="s2">&#34;</span> <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="o">[</span> <span class="s2">&#34;</span><span class="k">$(</span>stat -c%s <span class="s2">&#34;</span><span class="nv">$BACKUP_LOG_FILE</span><span class="s2">&#34;</span><span class="k">)</span><span class="s2">&#34;</span> -ge <span class="s2">&#34;</span><span class="nv">$max_size</span><span class="s2">&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> i in <span class="m">3</span> <span class="m">2</span> 1<span class="p">;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">            <span class="o">[</span> -f <span class="s2">&#34;</span><span class="si">${</span><span class="nv">BACKUP_LOG_FILE</span><span class="p">%.log</span><span class="si">}</span><span class="s2">.</span><span class="nv">$i</span><span class="s2">.gz&#34;</span> <span class="o">]</span> <span class="o">&amp;&amp;</span> mv <span class="s2">&#34;</span><span class="si">${</span><span class="nv">BACKUP_LOG_FILE</span><span class="p">%.log</span><span class="si">}</span><span class="s2">.</span><span class="nv">$i</span><span class="s2">.gz&#34;</span> <span class="s2">&#34;</span><span class="si">${</span><span class="nv">BACKUP_LOG_FILE</span><span class="p">%.log</span><span class="si">}</span><span class="s2">.</span><span class="k">$((</span>i <span class="o">+</span> <span class="m">1</span><span class="k">))</span><span class="s2">.gz&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="k">done</span>
</span></span><span class="line"><span class="cl">        gzip -c <span class="s2">&#34;</span><span class="nv">$BACKUP_LOG_FILE</span><span class="s2">&#34;</span> &gt;<span class="s2">&#34;</span><span class="si">${</span><span class="nv">BACKUP_LOG_FILE</span><span class="p">%.log</span><span class="si">}</span><span class="s2">.1.gz&#34;</span>
</span></span><span class="line"><span class="cl">        : &gt;<span class="s2">&#34;</span><span class="nv">$BACKUP_LOG_FILE</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">fi</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">run_rsync<span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="nb">local</span> <span class="nv">failures</span><span class="o">=</span><span class="m">0</span>
</span></span><span class="line"><span class="cl">    <span class="nb">local</span> <span class="nv">success</span><span class="o">=</span><span class="m">0</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="nv">IFS</span><span class="o">=</span> <span class="nb">read</span> -r src_path <span class="o">||</span> <span class="o">[[</span> -n <span class="s2">&#34;</span><span class="nv">$src_path</span><span class="s2">&#34;</span> <span class="o">]]</span><span class="p">;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">        <span class="o">[</span> -z <span class="s2">&#34;</span><span class="nv">$src_path</span><span class="s2">&#34;</span> <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="k">continue</span>
</span></span><span class="line"><span class="cl">        <span class="o">[</span> ! -e <span class="s2">&#34;</span><span class="nv">$src_path</span><span class="s2">&#34;</span> <span class="o">]</span> <span class="o">&amp;&amp;</span> log <span class="s2">&#34;SKIP&#34;</span> <span class="s2">&#34;Path not found: </span><span class="nv">$src_path</span><span class="s2">&#34;</span> <span class="o">&amp;&amp;</span> <span class="k">continue</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="o">[</span> <span class="s2">&#34;</span><span class="nv">$DRY_RUN</span><span class="s2">&#34;</span> -eq <span class="m">1</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">            <span class="nv">rsync_args</span><span class="o">=(</span>-anx --delete --exclude-from <span class="s2">&#34;</span><span class="nv">$EXCLUDE_FILE</span><span class="s2">&#34;</span> <span class="s2">&#34;</span><span class="nv">$src_path</span><span class="s2">&#34;</span> <span class="s2">&#34;</span><span class="nv">$BACKUP_DESTINATION</span><span class="s2">&#34;</span><span class="o">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">else</span>
</span></span><span class="line"><span class="cl">            <span class="nv">rsync_args</span><span class="o">=(</span>-axs --delete --exclude-from <span class="s2">&#34;</span><span class="nv">$EXCLUDE_FILE</span><span class="s2">&#34;</span> <span class="s2">&#34;</span><span class="nv">$src_path</span><span class="s2">&#34;</span> <span class="s2">&#34;</span><span class="nv">$BACKUP_DESTINATION</span><span class="s2">&#34;</span><span class="o">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="nv">RSYNC_OUTPUT</span><span class="o">=</span><span class="k">$(</span>rsync <span class="s2">&#34;</span><span class="si">${</span><span class="nv">rsync_args</span><span class="p">[@]</span><span class="si">}</span><span class="s2">&#34;</span> 2&gt;<span class="p">&amp;</span>1<span class="k">)</span>
</span></span><span class="line"><span class="cl">        <span class="nv">rsync_exit</span><span class="o">=</span><span class="nv">$?</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="o">[</span> <span class="nv">$rsync_exit</span> -eq <span class="m">0</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">            log <span class="s2">&#34;OK&#34;</span> <span class="s2">&#34;Synced: </span><span class="nv">$src_path</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="nv">success</span><span class="o">=</span><span class="k">$((</span>success <span class="o">+</span> <span class="m">1</span><span class="k">))</span>
</span></span><span class="line"><span class="cl">        <span class="k">else</span>
</span></span><span class="line"><span class="cl">            log <span class="s2">&#34;ERROR&#34;</span> <span class="s2">&#34;Failed: </span><span class="nv">$src_path</span><span class="s2">: </span><span class="nv">$RSYNC_OUTPUT</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="nv">failures</span><span class="o">=</span><span class="k">$((</span>failures <span class="o">+</span> <span class="m">1</span><span class="k">))</span>
</span></span><span class="line"><span class="cl">        <span class="k">fi</span>
</span></span><span class="line"><span class="cl">    <span class="k">done</span> &lt;<span class="s2">&#34;</span><span class="nv">$BACKUP_SOURCE_FILE</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;</span><span class="nv">$success</span><span class="s2"> success, </span><span class="nv">$failures</span><span class="s2"> failure(s)&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nv">$failures</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">main<span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    log_rotate
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Secure lockfile creation</span>
</span></span><span class="line"><span class="cl">    <span class="nb">umask</span> <span class="m">0077</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> ! touch <span class="s2">&#34;</span><span class="nv">$LOCKFILE</span><span class="s2">&#34;</span> 2&gt;/dev/null<span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">        <span class="nb">echo</span> <span class="s2">&#34;Error: Cannot create lockfile at </span><span class="nv">$LOCKFILE</span><span class="s2">&#34;</span> &gt;<span class="p">&amp;</span><span class="m">2</span>
</span></span><span class="line"><span class="cl">        <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl">    <span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Open the lock file and assign it to FD9</span>
</span></span><span class="line"><span class="cl">    <span class="nb">exec</span> 9&gt;<span class="s2">&#34;</span><span class="nv">$LOCKFILE</span><span class="s2">&#34;</span> <span class="o">||</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="nb">echo</span> <span class="s2">&#34;Error: Cannot open lockfile: </span><span class="nv">$LOCKFILE</span><span class="s2">&#34;</span> &gt;<span class="p">&amp;</span><span class="m">2</span>
</span></span><span class="line"><span class="cl">        <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Try to acquire the lock on FD9</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> ! flock -n 9<span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">        <span class="nb">echo</span> <span class="s2">&#34;Another backup is already running (lockfile in use).&#34;</span> &gt;<span class="p">&amp;</span><span class="m">2</span>
</span></span><span class="line"><span class="cl">        <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl">    <span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Ensure lockfile is deleted on exit</span>
</span></span><span class="line"><span class="cl">    cleanup<span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        rm -f <span class="s2">&#34;</span><span class="nv">$LOCKFILE</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="nb">trap</span> cleanup EXIT
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="o">[</span> ! -f <span class="s2">&#34;</span><span class="nv">$BACKUP_SOURCE_FILE</span><span class="s2">&#34;</span> <span class="o">]</span> <span class="o">&amp;&amp;</span> log <span class="s2">&#34;ERROR&#34;</span> <span class="s2">&#34;Missing source file&#34;</span> <span class="o">&amp;&amp;</span> <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl">    <span class="o">[</span> ! -f <span class="s2">&#34;</span><span class="nv">$EXCLUDE_FILE</span><span class="s2">&#34;</span> <span class="o">]</span> <span class="o">&amp;&amp;</span> log <span class="s2">&#34;ERROR&#34;</span> <span class="s2">&#34;Missing exclude file&#34;</span> <span class="o">&amp;&amp;</span> <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> ! mountpoint -q <span class="s2">&#34;</span><span class="nv">$BACKUP_MOUNT_POINT</span><span class="s2">&#34;</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">        log <span class="s2">&#34;ERROR&#34;</span> <span class="s2">&#34;Mount point </span><span class="nv">$BACKUP_MOUNT_POINT</span><span class="s2"> not found&#34;</span>
</span></span><span class="line"><span class="cl">        notify_pushover <span class="s2">&#34;Backup failed: </span><span class="nv">$BACKUP_MOUNT_POINT</span><span class="s2"> not mounted&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl">    <span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    log <span class="s2">&#34;START&#34;</span> <span class="s2">&#34;Rsync backup started (dry-run=</span><span class="nv">$DRY_RUN</span><span class="s2">)&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nv">result</span><span class="o">=</span><span class="k">$(</span>run_rsync<span class="k">)</span>
</span></span><span class="line"><span class="cl">    <span class="nv">status</span><span class="o">=</span><span class="nv">$?</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    log <span class="s2">&#34;END&#34;</span> <span class="s2">&#34;Backup complete. </span><span class="nv">$result</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="o">[</span> <span class="nv">$status</span> -ne <span class="m">0</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">        notify_pushover <span class="s2">&#34;Rsync backup failed: </span><span class="nv">$result</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;</span><span class="nv">$result</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">exit</span> <span class="nv">$status</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">main
</span></span></code></pre></div>]]></content:encoded>
    </item>
    <item>
      <title>SearXNG, ou comment remplacer Google search</title>
      <link>https://log.2027a.net/posts/searxng-ou-comment-remplacer-google-search/</link>
      <pubDate>Tue, 11 Feb 2025 19:42:38 -0500</pubDate>
      <guid>https://log.2027a.net/posts/searxng-ou-comment-remplacer-google-search/</guid>
      <description>&lt;p&gt;Si vous me lisez parfois, vous avez probablement remarqué que je suis très
attaché à la protection de la vie privée, et que je suis particulièrement
hostile aux services de Google, qui sont au moins autant invasifs qu&amp;rsquo;ils ne sont
utiles. J&amp;rsquo;ai très largement &amp;ldquo;dé-googlé&amp;rdquo; ma vie numérique en utilisant d&amp;rsquo;autres
services (souvent auto-hébergés) que ceux dont Google est le champion : Mes
données, mon courriel, mes notes, mes cartes, ma navigation, mes calendriers,
mes OTPs&amp;hellip; ne sont plus chez Google, mais soit sur mes propres machines, soit
chez des alternatives libres et chiffrées (Proton, OpenStreetMap)&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Si vous me lisez parfois, vous avez probablement remarqué que je suis très
attaché à la protection de la vie privée, et que je suis particulièrement
hostile aux services de Google, qui sont au moins autant invasifs qu&rsquo;ils ne sont
utiles. J&rsquo;ai très largement &ldquo;dé-googlé&rdquo; ma vie numérique en utilisant d&rsquo;autres
services (souvent auto-hébergés) que ceux dont Google est le champion : Mes
données, mon courriel, mes notes, mes cartes, ma navigation, mes calendriers,
mes OTPs&hellip; ne sont plus chez Google, mais soit sur mes propres machines, soit
chez des alternatives libres et chiffrées (Proton, OpenStreetMap)</p>
<p><img alt="SearxNG" loading="lazy" src="/img/searxng.png"></p>
<p>Restait, jusqu&rsquo;il y a peu, la recherche elle-même. On aime en dire du mal, et pour
l&rsquo;essentiel, c&rsquo;est mérité, mais le moteur de recherche de Google reste (et
amha, de loin) le meilleur disponible, et malgré mes tentatives persistantes
avec Ecosia, DuckDuckGo et autres BraveSearch, je finissait toujours par
revenir à ce bon vieux Google search, lui offrant sur un plateau mon profil
pour son algorithme de profilage C&rsquo;est maintenant chose du passé : je teste
depuis quelques mois SearXNG, un &ldquo;meta-moteur de recherche&rdquo; : <a href="https://searxng.org">SearXNG</a></p>
<p>En pratique, c&rsquo;est un système qui transfère votre requête à d&rsquo;autres moteurs
de recherches (incluant google search, parmi plus de 200
moteurs recensés), filtrant au passage toutes données privées, puis qui vous
retourne les résultats, là encore, en filtrant toute forme de tracking,
publicité et autres saloperies que les moteurs de recherches embarquent dans
leurs résultats. Tout ça étant évidement configurable en détails</p>
<p>Le résultat final est parfait à mon gout : j&rsquo;ai accès à toute la puissance de
Google search (combiné avec les résultats d&rsquo;autres moteurs, pour des résultats
encore plus riches), sans aucun des trucs qui me font habituellement pester,
depuis le tracking jusqu&rsquo;au AI slop, en passant par la publicité</p>
<p>Si vous êtes soucieux de votre vie privée sur internet, je vous conseille
vivement d&rsquo;essayer cette solution. Il existe des instances publiques, mais
c&rsquo;est typiquement le genre de truc qu&rsquo;il est vraiment plus pertinent d&rsquo;héberger
soi-même ou avec un groupe d&rsquo;ami.e.s de confiance. Quitter Google, ok, mais
pas pour se jeter dans les bras du 1er data brocker venu</p>
<p>La <a href="https://docs.searxng.org/admin/installation.html">procédure d&rsquo;installation</a>
n&rsquo;est pas très compliquée, mais requiert tout de même de comprendre un peu ce
que l&rsquo;on fait. Disons que si vous savez utiliser docker, c&rsquo;est très accessible,
et sinon, ça réclame un peu d&rsquo;apprentissage et/ou un nerd référent</p>
<h2 id="déployer-searxng-avec-docker-compose">Déployer SearXNG avec <code>docker compose</code></h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-compose.yaml" data-lang="compose.yaml"><span class="line"><span class="cl"><span class="nt">services</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">searxng</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">searxng/searxng</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">container_name</span><span class="p">:</span><span class="w"> </span><span class="l">searxng</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">restart</span><span class="p">:</span><span class="w"> </span><span class="l">unless-stopped</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">${PORT}:8080</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">volumes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">${SEARXNG_PATH}:/etc/searxng</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">environment</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">BASE_URL=https://my.url.tld/</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">INSTANCE_NAME=${INSTANCE_NAME}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">networks</span><span class="p">:</span><span class="w"> </span>{}<span class="w">
</span></span></span></code></pre></div>]]></content:encoded>
    </item>
  </channel>
</rss>
