<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Backup on Renne Rocha</title>
    <link>https://rennerocha.com/tags/backup/</link>
    <description>Recent content in Backup on Renne Rocha</description>
    <generator>Hugo</generator>
    <language>en</language>
    
      <managingEditor>blog@rocha.dev.br (Renne Rocha)</managingEditor>
    
    
      <webMaster>blog@rocha.dev.br (Renne Rocha)</webMaster>
    
    
    
      <lastBuildDate>Thu, 14 Aug 2025 00:00:00 +0000</lastBuildDate>
    
      <atom:link href="https://rennerocha.com/tags/backup/index.xml" rel="self" type="application/rss+xml" />
      <item>
        <title>Using rclone on Backblaze without spending all your money</title>
        <link>https://rennerocha.com/posts/using-rclone-on-backblaze-without-spending-all-your-money/</link>
        <pubDate>Thu, 14 Aug 2025 00:00:00 +0000</pubDate><author>blog@rocha.dev.br (Renne Rocha)</author>
        <guid>https://rennerocha.com/posts/using-rclone-on-backblaze-without-spending-all-your-money/</guid>
        <description>&lt;p&gt;&lt;a href=&#34;https://www.backblaze.com/&#34;&gt;Backblaze&lt;/a&gt; is an object storage company that offers a S3 compatible cloud object storage with very competitive prices. With prices starting at USD6 / TB / month (or less, as you pay proportionally to the amount of data stored), it was a perfect choice to store live data and backups of my projects and self-hosted services.&lt;/p&gt;&#xA;&lt;p&gt;One of the services that I use is &lt;a href=&#34;https://immich.app/&#34;&gt;Immich&lt;/a&gt;, a self-hosted photo and video management solution that allows me to have fully control on my photos (even in mobile). It doesn&amp;rsquo;t allows me to store my photos directly in a object storage as Backblaze, but I decided that it would be a good idea to keep a backup of the files of the server (photos, videos, and some other data that I need to keep safe).&lt;/p&gt;&#xA;&lt;p&gt;One tool that simplifies the process is &lt;a href=&#34;https://rclone.org/&#34;&gt;rclone&lt;/a&gt;, a tool that make it easier to sync data in cloud providers (their maintainers call it as &amp;ldquo;rsync for cloud storage&amp;rdquo;). I followed of the instructions they provide to &lt;a href=&#34;https://rclone.org/b2/&#34;&gt;configure Backblaze&lt;/a&gt; and then I was able to run the following command:&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;rclone sync /home/immich/library/ b2:my-immich-backup-bucket&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;This command syncs automatically only new and changed files (e.g. updated photos metadata), reducing the time needed to backup my photos and keeping them updated, uploading to my Backblaze bucket.&lt;/p&gt;&#xA;&lt;p&gt;There are many other options that I can use with rclone, and scenarios where this backup scheme doesn&amp;rsquo;t cover. However, as a starting point to give me a bit more of confidence that I will not lose my photos if my VPS provider suddenly decides to stop providing me services, it is good enough.&lt;/p&gt;&#xA;&lt;p&gt;I configured &lt;a href=&#34;https://www.man7.org/linux/man-pages/man5/crontab.5.html&#34;&gt;crontab&lt;/a&gt; to run it hourly, and then I start to get my files synchronized.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;So I got the first charge after enabling this scheme!&lt;/strong&gt; Usually I was paying less than USD2/month, but the first month I got a USD10 bill, then USD60, then USD70. I wasn´t storing terabytes of data. Looking at my billing report I noticed that now I have a not negligible amount of &lt;strong&gt;Class C (Charged)&lt;/strong&gt; transactions, that are responsible for me to pay that much.&lt;/p&gt;&#xA;&lt;p&gt;We interact with Backblaze using API calls to manage our storage, so we can upload and download files, get information about them and the buckets, and many other actions. Each call is grouped in a different class of transaction (A, B and C), and they are &lt;a href=&#34;https://www.backblaze.com/cloud-storage/transaction-pricing&#34;&gt;charged&lt;/a&gt; differently. Class A transactions are always free, but Class B and C transactions have a daily limit of 2,500 calls each and they charge for extra calls.&lt;/p&gt;&#xA;&lt;p&gt;When rclone executes, it gets information from the remote location about the files already there to decide if the local files need to be uploaded or not. This is done by the Class C API call &lt;a href=&#34;https://www.backblaze.com/apidocs/b2-list-file-names&#34;&gt;&lt;code&gt;b2_list_file_names&lt;/code&gt;&lt;/a&gt; that returns a list of filenames in chunks of 1.000 files each call.&lt;/p&gt;&#xA;&lt;p&gt;But when we have our files in nested directories, a call is made for each of them. Immich organizes the uploaded files in hundreds of nested directories, and because of that, every time my cron was executed, thousands of &lt;code&gt;b2_list_file_names&lt;/code&gt; calls were made. Given that I was running it hourly, I reached 17M calls in a month easily (around 500k a day). As I have only 2.5K free calls, I found the reason of the amount I was charged.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img loading=&#34;lazy&#34; &#xA;    src=&#34;https://rennerocha.com/immich-nested-directories.png&#34; &#xA;    alt=&#34;Immich nested directories&#34; &#xA;     &#xA;    width=924 &#xA;    height=&#34;215&#34;  /&gt;&lt;/p&gt;&#xA;&lt;p&gt;The solution is to add &lt;a href=&#34;https://rclone.org/docs/#fast-list&#34;&gt;&lt;code&gt;--fast-list&lt;/code&gt;&lt;/a&gt; parameter to the rclone command, that requires fewer transactions for highly recursive operations as we have in this scenario. This parameter &lt;a href=&#34;https://rclone.org/b2/&#34;&gt;is mentioned&lt;/a&gt; in rclone documentation that explains how to setup it with Backblaze, but I didn&amp;rsquo;t consider that this is basically a mandatory parameter based on how things are charged there!&lt;/p&gt;&#xA;&lt;p&gt;So I updated my rclone command to:&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;rclone sync /home/immich/library/ b2:my-immich-backup-bucket --fast-list&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;I reactivated cron again and after almost a day, instead of 500K calls, less than 1K calls were made (which is within the free amount I am entitled to). Probably when I have the double of the number of files I have right now, the daily quota I have will not be enough, but then I will know how to deal with it (possibly with a better backup strategy).&lt;/p&gt;&#xA;</description>
      </item>
      <item>
        <title>Creating backups for fly.io Volumes</title>
        <link>https://rennerocha.com/posts/creating-backups-for-fly-io-volumes/</link>
        <pubDate>Wed, 30 Apr 2025 00:00:00 +0000</pubDate><author>blog@rocha.dev.br (Renne Rocha)</author>
        <guid>https://rennerocha.com/posts/creating-backups-for-fly-io-volumes/</guid>
        <description>&lt;p&gt;I have a few applications running on &lt;a href=&#34;https://fly.io&#34;&gt;fly.io&lt;/a&gt;, and some of them need to&#xA;keep data in the file system persistently (more precisely, an SQLite database file and&#xA;user-submitted data) so that it is not lost after a redeploy or when the Fly Machine running my application is&#xA;restarted.&lt;/p&gt;&#xA;&lt;p&gt;To achieve that, I use &lt;a href=&#34;https://fly.io/docs/volumes/overview/&#34;&gt;Fly Volumes&lt;/a&gt; which are local&#xA;persistent storage for Fly Machines, mounted in my server just like a regular directory. This setup works fine,&#xA;but I began considering how to back up the data stored there.&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://fly.io/docs/volumes/snapshots/&#34;&gt;Volume snapshots&lt;/a&gt; are created automatically on a daily basis and&#xA;retained for 5 days by default. However, there doesn&amp;rsquo;t seem to be an easy (or well-documented) way to implement&#xA;a custom backup policy. I wanted the ability to copy the entire directory&amp;rsquo;s content using tools like &lt;code&gt;rsync&lt;/code&gt; or upload&#xA;it to an S3 bucket on my own schedule.&lt;/p&gt;&#xA;&lt;p&gt;I explored solutions involving &lt;code&gt;cron&lt;/code&gt; jobs running inside my Fly Machine, but they became overly complicated.&#xA;These approaches required modifying my &lt;code&gt;Dockerfile&lt;/code&gt; to install additional applications, and I wasn&amp;rsquo;t sure&#xA;how to manage the schedule effectively, especially since I configured my machines to auto-stop to save resources.&lt;/p&gt;&#xA;&lt;p&gt;Direct SSH connections requires me to use &lt;code&gt;flyctl&lt;/code&gt; CLI and it wasn&amp;rsquo;t clear to me how to handle authentication&#xA;in this case. After some research, I found that I can use &lt;a href=&#34;https://fly.io/docs/security/tokens/&#34;&gt;access tokens&lt;/a&gt;&#xA;to connect to the machines using SSH allowing me to send commands there in an automated way.&lt;/p&gt;&#xA;&lt;h2 id=&#34;generating-your-access-token&#34;&gt;&#xA;    &lt;a href=&#34;#generating-your-access-token&#34; class=&#34;anchor&#34;&gt;&#xA;        &lt;svg class=&#34;icon&#34; aria-hidden=&#34;true&#34; focusable=&#34;false&#34; height=&#34;16&#34; version=&#34;1.1&#34; viewBox=&#34;0 0 16 16&#34; width=&#34;16&#34;&gt;&#xA;            &lt;path fill-rule=&#34;evenodd&#34;&#xA;                d=&#34;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&#34;&gt;&#xA;            &lt;/path&gt;&#xA;        &lt;/svg&gt;&#xA;    &lt;/a&gt;&#xA;    Generating your access token&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;First step is to create an access token that allows me to send commands to my machine without requiring any&#xA;manual form of authentication. This can be done using the following command:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&#xA;&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;1&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#xA;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fly tokens create ssh -n my-token-name&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&lt;p&gt;Check the &lt;a href=&#34;https://fly.io/docs/flyctl/tokens-create-ssh/&#34;&gt;command documentation&lt;/a&gt; for more options. The output of&#xA;this command will be as the following, where &lt;code&gt;&amp;lt;TOKEN_CONTENT_STRING&amp;gt;&lt;/code&gt; will be a very long string that&#xA;you need to store and don&amp;rsquo;t share it publicly.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&#xA;&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;1&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#xA;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;FlyV1 &amp;lt;TOKEN_CONTENT_STRING&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&lt;p&gt;Add the token to an environment var in the machine you will run the backup script:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&#xA;&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;1&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#xA;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;export FLY_SSH_TOKEN&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&amp;lt;TOKEN_CONTENT_STRING&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&lt;h2 id=&#34;data-location&#34;&gt;&#xA;    &lt;a href=&#34;#data-location&#34; class=&#34;anchor&#34;&gt;&#xA;        &lt;svg class=&#34;icon&#34; aria-hidden=&#34;true&#34; focusable=&#34;false&#34; height=&#34;16&#34; version=&#34;1.1&#34; viewBox=&#34;0 0 16 16&#34; width=&#34;16&#34;&gt;&#xA;            &lt;path fill-rule=&#34;evenodd&#34;&#xA;                d=&#34;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&#34;&gt;&#xA;            &lt;/path&gt;&#xA;        &lt;/svg&gt;&#xA;    &lt;/a&gt;&#xA;    Data location&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;The volume is mounted in &lt;code&gt;/data&lt;/code&gt; directory, defined in our application &lt;code&gt;fly.toml&lt;/code&gt; file:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[[mounts]]&#xA;  source = &amp;#39;app_data&amp;#39;&#xA;  destination = &amp;#39;/data&amp;#39;&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;creating-a-backup-script&#34;&gt;&#xA;    &lt;a href=&#34;#creating-a-backup-script&#34; class=&#34;anchor&#34;&gt;&#xA;        &lt;svg class=&#34;icon&#34; aria-hidden=&#34;true&#34; focusable=&#34;false&#34; height=&#34;16&#34; version=&#34;1.1&#34; viewBox=&#34;0 0 16 16&#34; width=&#34;16&#34;&gt;&#xA;            &lt;path fill-rule=&#34;evenodd&#34;&#xA;                d=&#34;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&#34;&gt;&#xA;            &lt;/path&gt;&#xA;        &lt;/svg&gt;&#xA;    &lt;/a&gt;&#xA;    Creating a backup script&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Given that we have a token, we are now able to execute SSH commands remotely on our Fly Machine. In&#xA;our scenario, I am compacting the whole content of &lt;code&gt;/data/&lt;/code&gt; directory (where all the data that I want&#xA;to backup is located) generating a tarball, then I download it locally.&lt;/p&gt;&#xA;&lt;p&gt;I could create a custom script and copy it to the remote machine if I want to perform more complex&#xA;tasks, or you can extend/modify this script to perform other tasks (e.g. download the tarball&#xA;and upload to a S3 bucket).&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;#!/bin/bash&#xA;# backup.sh&#xA;&#xA;# Need to start container in fly.io if it was stopped by inactivity&#xA;curl -s -o /dev/null https://your-app.fly.dev/&#xA;&#xA;filename=&amp;#34;data_backup_$(date +%F).tar.gz&amp;#34;&#xA;fly ssh console -C &amp;#39;tar cvz /data&amp;#39; -t $FLY_SSH_TOKEN &amp;gt; $filename&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;run-it-periodically&#34;&gt;&#xA;    &lt;a href=&#34;#run-it-periodically&#34; class=&#34;anchor&#34;&gt;&#xA;        &lt;svg class=&#34;icon&#34; aria-hidden=&#34;true&#34; focusable=&#34;false&#34; height=&#34;16&#34; version=&#34;1.1&#34; viewBox=&#34;0 0 16 16&#34; width=&#34;16&#34;&gt;&#xA;            &lt;path fill-rule=&#34;evenodd&#34;&#xA;                d=&#34;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&#34;&gt;&#xA;            &lt;/path&gt;&#xA;        &lt;/svg&gt;&#xA;    &lt;/a&gt;&#xA;    Run it periodically&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Now you can add &lt;code&gt;backup.sh&lt;/code&gt; to your &lt;code&gt;crontab&lt;/code&gt; schedule, or even adapt the procedure described here&#xA;to be executed in other environments, like defining a GitHub Action or another way to schedule jobs.&lt;/p&gt;&#xA;&lt;p&gt;I know this is not the most complete way to implement a backup policy, but it is working for my current projects.&#xA;In the future, as I improve my scripts, I will possibly update this post to make it more complete.&lt;/p&gt;&#xA;</description>
      </item>
  </channel>
</rss>