<?xml version="1.0" encoding="utf-8" standalone="yes"?><feed xmlns="http://www.w3.org/2005/Atom">
  <title></title>
  <subtitle></subtitle>
  <id>https://www.endpointdev.com/blog/tags/storage/</id>
  <link href="https://www.endpointdev.com/blog/tags/storage/"/>
  <link href="https://www.endpointdev.com/blog/tags/storage/" rel="self"/>
  <updated>2025-06-02T00:00:00+00:00</updated>
  <author>
    <name>End Point Dev</name>
  </author>
  
    <entry>
      <title>Implementing Azure Blob Storage in .NET 9</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2025/06/implementing-azure-blob-storage-net-9/"/>
      <id>https://www.endpointdev.com/blog/2025/06/implementing-azure-blob-storage-net-9/</id>
      <published>2025-06-02T00:00:00+00:00</published>
      <author>
        <name>Juan Pablo Ventoso</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2025/06/implementing-azure-blob-storage-net-9/water-and-wood-dock.webp&#34; alt=&#34;Shallow water and an old wood dock in southern Patagonia, Argentina&#34;&gt;&lt;/p&gt;
&lt;!-- Photo by Juan Pablo Ventoso, 2022. --&gt;
&lt;p&gt;Businesses keep moving toward scalable and cloud-based architectures. With this in mind, a client that was dealing with random errors in a &lt;a href=&#34;https://dotnet.microsoft.com/&#34;&gt;.NET&lt;/a&gt; app when saving files locally on the web server decided to get rid of that process and replace it with an &lt;a href=&#34;https://azure.microsoft.com/en-us/products/storage/blobs&#34;&gt;Azure Blob Storage&lt;/a&gt; implementation.&lt;/p&gt;
&lt;p&gt;Why use Azure Blob Storage? It&amp;rsquo;s an efficient cloud object storage solution from Microsoft, designed to store unstructured data, optimized for storing and serving documents, media, logs, or binary data, especially in applications that expose this data through an API. The key features are high performance, redundancy, reliability, and scalability. There&amp;rsquo;s an SDK that we can use for easy integration and development, be it in .NET or other languages.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s take a look at what that change involves. For this example, we will set up the integration in a .NET 9 application:&lt;/p&gt;
&lt;p&gt;Install the &lt;a href=&#34;https://www.nuget.org/&#34;&gt;NuGet&lt;/a&gt; package required to connect to Azure Blob Storage. We can do it with the &lt;a href=&#34;https://learn.microsoft.com/en-us/dotnet/core/tools/&#34;&gt;dotnet CLI&lt;/a&gt;, or through the NuGet package manager.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;dotnet add package Azure.Storage.Blobs&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then, we need to configure our connection in our appsettings.json file. We will use the connection string that Azure provides us when we create the new storage account.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;AzureBlobStorage&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;ConnectionString&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;{your-connection-string}&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;ContainerName&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;my-container&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In Azure Blob Storage, containers are like folders that group blobs (or files) together within a storage account. In this setting, the &lt;code&gt;ContainerName&lt;/code&gt; value tells our application which container inside that account will be used for storing and retrieving blobs.&lt;/p&gt;
&lt;p&gt;Create a &lt;code&gt;BlobService&lt;/code&gt; class to read and write to the container.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;using&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Azure.Storage.Blobs&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;using&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Microsoft.Extensions.Configuration&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;BlobService&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;readonly&lt;/span&gt; BlobContainerClient _containerClient;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#c00;background-color:#fff0f0;font-weight:bold&#34;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#c00;background-color:#fff0f0;font-weight:bold&#34;&gt;/// Initializes a new instance of the &amp;lt;see cref=&amp;#34;BlobService&amp;#34;/&amp;gt; class.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#c00;background-color:#fff0f0;font-weight:bold&#34;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#c00;background-color:#fff0f0;font-weight:bold&#34;&gt;/// &amp;lt;param name=&amp;#34;configuration&amp;#34;&amp;gt;Application configuration containing Azure Blob Storage settings.&amp;lt;/param&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; BlobService(IConfiguration configuration)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;var&lt;/span&gt; connectionString = configuration[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;AzureBlobStorage:ConnectionString&amp;#34;&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;var&lt;/span&gt; containerName = configuration[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;AzureBlobStorage:ContainerName&amp;#34;&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        _containerClient = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; BlobContainerClient(connectionString, containerName);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#c00;background-color:#fff0f0;font-weight:bold&#34;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#c00;background-color:#fff0f0;font-weight:bold&#34;&gt;/// Uploads a file stream to Azure Blob Storage with the specified file name.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#c00;background-color:#fff0f0;font-weight:bold&#34;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#c00;background-color:#fff0f0;font-weight:bold&#34;&gt;/// &amp;lt;param name=&amp;#34;fileName&amp;#34;&amp;gt;The name of the file to be stored in blob storage.&amp;lt;/param&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#c00;background-color:#fff0f0;font-weight:bold&#34;&gt;/// &amp;lt;param name=&amp;#34;fileStream&amp;#34;&amp;gt;The stream representing the file content.&amp;lt;/param&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#c00;background-color:#fff0f0;font-weight:bold&#34;&gt;/// &amp;lt;returns&amp;gt;A task that represents the asynchronous upload operation.&amp;lt;/returns&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;async&lt;/span&gt; Task UploadFileAsync(&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;string&lt;/span&gt; fileName, Stream fileStream)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;var&lt;/span&gt; blobClient = _containerClient.GetBlobClient(fileName);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;await&lt;/span&gt; blobClient.UploadAsync(fileStream, overwrite: &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#c00;background-color:#fff0f0;font-weight:bold&#34;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#c00;background-color:#fff0f0;font-weight:bold&#34;&gt;/// Downloads a file from Azure Blob Storage.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#c00;background-color:#fff0f0;font-weight:bold&#34;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#c00;background-color:#fff0f0;font-weight:bold&#34;&gt;/// &amp;lt;param name=&amp;#34;fileName&amp;#34;&amp;gt;The name of the file to download from blob storage.&amp;lt;/param&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#c00;background-color:#fff0f0;font-weight:bold&#34;&gt;/// &amp;lt;returns&amp;gt;A task that returns a stream containing the blob&amp;#39;s content.&amp;lt;/returns&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;async&lt;/span&gt; Task&amp;lt;Stream&amp;gt; DownloadFileAsync(&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;string&lt;/span&gt; fileName)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;var&lt;/span&gt; blobClient = _containerClient.GetBlobClient(fileName);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;var&lt;/span&gt; response = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;await&lt;/span&gt; blobClient.DownloadAsync();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; response.Value.Content;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Quite simple, isn&amp;rsquo;t it? Now, let&amp;rsquo;s see a way to easily expose file upload/​download endpoints via ASP.NET Core &lt;a href=&#34;https://learn.microsoft.com/en-us/aspnet/core/tutorials/min-web-api&#34;&gt;minimal APIs&lt;/a&gt;. Introduced in .NET 6 and evolving further in .NET 9, minimal APIs provide a lightweight way to build HTTP APIs with a few lines of code. Instead of requiring full controller classes and attributes, minimal APIs let you define routes directly in your main &lt;code&gt;Program.cs&lt;/code&gt; file, using simple &lt;a href=&#34;https://learn.microsoft.com/dotnet/csharp/language-reference/operators/lambda-expressions&#34;&gt;lambda expressions&lt;/a&gt;. This makes them ideal for microservices, lightweight APIs, and internal tools where fast development and low overhead are essential.&lt;/p&gt;
&lt;p&gt;For this example, we will create two endpoints that will use our BlobService class to communicate with Azure Blob Storage to save and retrieve files. In our &lt;code&gt;Program.cs&lt;/code&gt; file, let&amp;rsquo;s add:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#c00;background-color:#fff0f0;font-weight:bold&#34;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#c00;background-color:#fff0f0;font-weight:bold&#34;&gt;/// Endpoint to upload a file to Azure Blob Storage.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#c00;background-color:#fff0f0;font-weight:bold&#34;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#c00;background-color:#fff0f0;font-weight:bold&#34;&gt;/// &amp;lt;param name=&amp;#34;file&amp;#34;&amp;gt;The uploaded file sent from the client (via multipart/form-data).&amp;lt;/param&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#c00;background-color:#fff0f0;font-weight:bold&#34;&gt;/// &amp;lt;param name=&amp;#34;blobService&amp;#34;&amp;gt;Injected service used to handle blob storage operations.&amp;lt;/param&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#c00;background-color:#fff0f0;font-weight:bold&#34;&gt;/// &amp;lt;returns&amp;gt;An HTTP 200 OK response when the upload succeeds.&amp;lt;/returns&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;app.MapPost(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;/upload&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;async&lt;/span&gt; (IFormFile file, BlobService blobService) =&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;using&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;var&lt;/span&gt; stream = file.OpenReadStream();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;await&lt;/span&gt; blobService.UploadFileAsync(file.FileName, stream);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; Results.Ok(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;File uploaded successfully&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#c00;background-color:#fff0f0;font-weight:bold&#34;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#c00;background-color:#fff0f0;font-weight:bold&#34;&gt;/// Endpoint to download a file from Azure Blob Storage.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#c00;background-color:#fff0f0;font-weight:bold&#34;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#c00;background-color:#fff0f0;font-weight:bold&#34;&gt;/// &amp;lt;param name=&amp;#34;fileName&amp;#34;&amp;gt;The name of the file to download (provided in the URL path).&amp;lt;/param&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#c00;background-color:#fff0f0;font-weight:bold&#34;&gt;/// &amp;lt;param name=&amp;#34;blobService&amp;#34;&amp;gt;Injected service used to access blob storage.&amp;lt;/param&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#c00;background-color:#fff0f0;font-weight:bold&#34;&gt;/// &amp;lt;returns&amp;gt;The file stream as a binary response with a download filename.&amp;lt;/returns&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;app.MapGet(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;/download/{fileName}&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;async&lt;/span&gt; (&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;string&lt;/span&gt; fileName, BlobService blobService) =&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;var&lt;/span&gt; stream = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;await&lt;/span&gt; blobService.DownloadFileAsync(fileName);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; Results.File(stream, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;application/octet-stream&amp;#34;&lt;/span&gt;, fileName);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Of course, we would want to configure authentication, add validations, and other security measures on these endpoints to prevent spammers from filling out our storage capacity 🙂. But the basics are there.&lt;/p&gt;
&lt;p&gt;Also, there are recommended practices for any Azure integrations to consider, as we increase the robustness of our application:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Monitor and log all storage operations using &lt;a href=&#34;https://learn.microsoft.com/azure/azure-monitor/app/app-insights-overview&#34;&gt;Azure Application Insights&lt;/a&gt;. Integrate Application Insights into our .NET app to get detailed logging of the different HTTP calls to the Azure services.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Use &lt;a href=&#34;https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/overview&#34;&gt;Managed Identities&lt;/a&gt;: Instead of storing secrets in appsettings.json, we can assign a Managed Identity to our application service, and grant it the Storage Blob Data Contributor role at the storage account. That will allow the application to gain access to the container automatically, without storing credentials in our settings files.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Containers should be private by default. Avoid using &amp;ldquo;Blob&amp;rdquo; or &amp;ldquo;Container&amp;rdquo; public access levels, unless necessary. When public access is enabled, any person with a link to the container will be able to open or download the blob.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We will cover more details about using Managed Identities and &lt;a href=&#34;https://learn.microsoft.com/en-us/azure/storage/common/storage-sas-overview&#34;&gt;Shared Access Signatures (SAS)&lt;/a&gt; for other Azure services in an upcoming blog post. Stay tuned!&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Media erasure in the time of SSD</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2020/12/drive-destruction/"/>
      <id>https://www.endpointdev.com/blog/2020/12/drive-destruction/</id>
      <published>2020-12-10T00:00:00+00:00</published>
      <author>
        <name>Ardyn Majere</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2020/12/drive-destruction/garbage.jpg&#34; alt=&#34;&#34;&gt;
&lt;a href=&#34;https://www.pexels.com/photo/garbage-lot-2967770/&#34;&gt;Photo&lt;/a&gt; by &lt;a href=&#34;https://www.pexels.com/@alexfu&#34;&gt;Alex Fu&lt;/a&gt; from Pexels&lt;/p&gt;
&lt;p&gt;How valuable is your data? Losing it to a third party is usually a business’s worst nightmare—​and can cause legal or even criminal repercussions, depending on the drive’s contents and the business’s jurisdiction.&lt;/p&gt;
&lt;p&gt;Every system adminstrator worth their salt knows that running “rm” (or equivalent delete operations) doesn’t actually remove data, it simply removes the file name from the filesystem and leaves the data in place on the disk.&lt;/p&gt;
&lt;p&gt;When dealing with traditional storage, destroying (intentionally or otherwise) your data used to be relatively easy. A wise system admin could simply run:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#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;shred /dev/sda&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And be fairly certain of the result. A cautious one might run a demagnetizing wand over the drive. Only the most paranoid might destroy it physically.&lt;/p&gt;
&lt;h3 id=&#34;the-age-of-ssds&#34;&gt;The Age of SSDs&lt;/h3&gt;
&lt;p&gt;Nowadays, most servers have switched away from storing data on rotating metal or glass platters. Solid state drives, or SSDs, are faster, less prone to errors from physical impact, and generally more sought after.&lt;/p&gt;
&lt;p&gt;SSDs have issues with speed if the drives are too full, and have a limited lifespan—​only a certain number of write operations can be achieved. This is less of an issue with modern drives thanks to wear leveling built into the firmware of the drives. However, this leads to some issues as well.&lt;/p&gt;
&lt;h3 id=&#34;complicated-systems-introduce-issues&#34;&gt;Complicated systems introduce issues&lt;/h3&gt;
&lt;p&gt;Because SSDs manage which blocks of storage they write to, a simple shred won’t do. There could be hundreds of bytes, or even kilobytes or megabytes, of data that the shred doesn’t reach.&lt;/p&gt;
&lt;p&gt;Even some “traditional” storage can run into such issues these days. Hybrid drives offer some speed advantages: By leveraging a small amount of SSD storage, these drives save data to SSD first, then write it at slower speeds to the actual magnetic platters. The same issues with SSD storage can affect this cache of data.&lt;/p&gt;
&lt;p&gt;So how to be sure?&lt;/p&gt;
&lt;p&gt;Ideally, I would recommend using a combination of methods for security. Here are the main methods that are used at present:&lt;/p&gt;
&lt;h3 id=&#34;run-shred-and-hope-for-the-best&#34;&gt;Run shred and hope for the best&lt;/h3&gt;
&lt;p&gt;This is an option, for sure. Writing to every block of the disk will generally wipe the data securely enough. However, if there are sectors that have been marked bad by the drive firmware, these won’t be covered.&lt;/p&gt;
&lt;h3 id=&#34;nwipe-dban-and-other-free-options&#34;&gt;nwipe, DBAN, and other free options&lt;/h3&gt;
&lt;p&gt;Free software exists to securely run shred over operating systems. The old gold standard for this was Darik’s Boot and Nuke (DBAN), written by Darik Horn, but this software was acquired by Blancco, a for-profit data erasure company (more on them later). DBAN is &lt;a href=&#34;https://dban.org/&#34;&gt;still available&lt;/a&gt;, but lacks features necessary for a 100% wipe.&lt;/p&gt;
&lt;p&gt;A fork called &lt;a href=&#34;https://github.com/martijnvanbrummelen/nwipe&#34;&gt;nwipe&lt;/a&gt; exists, and is available on many live operating systems. nwipe does a more thorough job than shred, but it still can’t get to sectors the firmware hides from the operating system.&lt;/p&gt;
&lt;p&gt;Wikipedia has a &lt;a href=&#34;https://en.wikipedia.org/wiki/List_of_data-erasing_software&#34;&gt;list of data-erasing software&lt;/a&gt;. Most of these are open source or freeware, but it includes a few paid options.&lt;/p&gt;
&lt;h3 id=&#34;sata-secure-erase-with-hdparm&#34;&gt;SATA secure erase with hdparm&lt;/h3&gt;
&lt;p&gt;Drive manufacturers have thought of this issue as well. Most drive manufacturers offer a secure erase program that works with their drives, and the &lt;a href=&#34;https://en.wikipedia.org/wiki/Serial_ATA&#34;&gt;SATA&lt;/a&gt; standard has a procedure in place for &lt;a href=&#34;https://ata.wiki.kernel.org/index.php/ATA_Secure_Erase&#34;&gt;securely erasing drive contents&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#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;hdparm --user-master u --security-set-pass TheEnd /dev/X
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;hdparm -I /dev/X  &lt;span style=&#34;color:#888&#34;&gt;# Check that the master password is enabled&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;time&lt;/span&gt; hdparm --user-master u --security-erase TheEnd /dev/X&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will show some output indicating success or failure. Once this is done…&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#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;hdparm -I /dev/X  &lt;span style=&#34;color:#888&#34;&gt;# Check that the master password is not enabled, which indicates the wipe was successful&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is likely the best option for a savvy home user, combined with shred/​nwipe. However, if you are going to attempt this, I highly recommend &lt;a href=&#34;https://ata.wiki.kernel.org/index.php/ATA_Secure_Erase&#34;&gt;reading the full instructions&lt;/a&gt;!&lt;/p&gt;
&lt;h3 id=&#34;commercial-software&#34;&gt;Commercial software&lt;/h3&gt;
&lt;p&gt;Blancco, aforementioned as the purchasers of DBAN, offer an enterprise level product for destroying data called &lt;a href=&#34;https://www.blancco.com/products/drive-eraser/&#34;&gt;Drive Eraser&lt;/a&gt;. Importantly, for business users, it provides certification that the data are gone for good.&lt;/p&gt;
&lt;p&gt;There are also many other options. Ask your favourite security vendor and they will be happy to sell you a product for this, though you usually have to take them at their word.&lt;/p&gt;
&lt;h3 id=&#34;be-preparedencrypt-your-disk&#34;&gt;Be prepared—​encrypt your disk!&lt;/h3&gt;
&lt;p&gt;Encrypt your disk. This can be achieved by encrypting either your home folder or by encrypting your whole disk. One downside is that your password is required after every reboot—​a problem especially for servers, but even cloud providers offer virtual terminals these days, so for secure operations, this is the best option.&lt;/p&gt;
&lt;p&gt;A secure, long password is usually enough to ensure the disk can’t be cracked, though be aware that cybersecurity changes quickly—​what’s impossible to brute-force today might not be in five years.&lt;/p&gt;
&lt;p&gt;So how can we be really, really sure?&lt;/p&gt;
&lt;h3 id=&#34;thermite&#34;&gt;Thermite&lt;/h3&gt;
&lt;p&gt;Physical destruction of the media is always the most secure way to destroy a disk—​crushing, drilling, or in the fanciful, dangerous dreams of some systems administrators, covering the disk in thermite and lighting it with a magnesium flare. &lt;strong&gt;(Do not actually use thermite.)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;At home, &lt;a href=&#34;https://www.myfixguide.com/samsung-860-pro-ssd-teardown/&#34;&gt;disassembling the drive&lt;/a&gt; and taking a hammer to the data bearing chips will do the trick, though again, combine this with the above options to be sure.&lt;/p&gt;
&lt;p&gt;For spinning disks, a similar procedure is advised, though there are other options, of course…&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/12/drive-destruction/drive_destruction.jpg&#34; alt=&#34;a drive with several slugs embedded in it&#34;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Check your local laws and follow all safety procedures before engaging in creative drive destruction techniques!&lt;/em&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;h3 id=&#34;final-thoughts&#34;&gt;Final thoughts&lt;/h3&gt;
&lt;p&gt;Use any or all of the above options and you’ll be ahead of the game. Taking the time to sanitize your data, or better, encrypting it from the beginning, is always a good investment.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Introduction to BorgBackup</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2020/09/introduction-to-borg-backup/"/>
      <id>https://www.endpointdev.com/blog/2020/09/introduction-to-borg-backup/</id>
      <published>2020-09-10T00:00:00+00:00</published>
      <author>
        <name>Kannan Ponnusamy</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2020/09/introduction-to-borg-backup/image-1.jpg&#34; alt=&#34;Black and silver hard drive&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://unsplash.com/photos/ShHkXuZdpTw&#34;&gt;Photo&lt;/a&gt; by &lt;a href=&#34;https://unsplash.com/@frank041985&#34;&gt;Frank R&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&#34;what-is-borg&#34;&gt;What is Borg?&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;https://www.borgbackup.org/&#34;&gt;BorgBackup&lt;/a&gt; (Borg for short) is a ‘deduplicating’ backup program that eliminates duplicate or redundant information. It optionally supports compression and authenticated encryption.&lt;/p&gt;
&lt;p&gt;The main objective of Borg is to provide an efficient and secure way to backup data. The deduplication technique utilized to produce the backup process is very quick and effective.&lt;/p&gt;
&lt;h4 id=&#34;step-1-install-the-borg-backups&#34;&gt;Step 1: Install the Borg backups&lt;/h4&gt;
&lt;p&gt;On Ubuntu/Debian:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#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;apt install borgbackup&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;On RHEL/CentOS/Fedora:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#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;dnf install borgbackup&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;step-2-initialize-local-borg-repository&#34;&gt;Step 2: Initialize Local Borg repository&lt;/h4&gt;
&lt;p&gt;Firstly, the system that is going to be backed up needs a new designated backup directory. Name the parent directory ‘backup’ and then create a child directory called ‘borgdemo’, which serves as the repository.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#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;mkdir -p /mnt/backup
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;borg init --encryption=repokey /mnt/backup/borgdemo&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;step-3-lets-create-the-first-backup-archive&#34;&gt;Step 3: Let’s create the first backup (archive)&lt;/h4&gt;
&lt;p&gt;In Borg terms, each backup instance will be called an archive. The following demonstrates how to backup the ‘photos’ directory and designate the archive as ‘archive_1’.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#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;borg create --stats --progress /mnt/backup/borgdemo::archive_1 /home/kannan/photos&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Note: the archive label for each backup run needs to be specified.&lt;/p&gt;
&lt;h4 id=&#34;step-4-next-backup-incremental&#34;&gt;Step 4: Next backup (Incremental)&lt;/h4&gt;
&lt;p&gt;In order to see if the run was successful, the same command will be executed again. However, this time, with the different unique archive label.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#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;borg create --stats --progress /mnt/backup/borgdemo::archive_2 /home/kannan/photos&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The following backup is mostly identical to the previous one. Because of deduplication, the process will not only run faster this time, it will be incremental as well. The &lt;code&gt;--stats&lt;/code&gt; flag will provide statistics regarding the size of deduplication.&lt;/p&gt;
&lt;h4 id=&#34;step-5-list-all-the-archives&#34;&gt;Step 5: List all the archives&lt;/h4&gt;
&lt;p&gt;The ‘borg list’ command lists all of the archives stored within the Borg repository.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#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;borg list /mnt/backup/borgdemo&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;step-6-remote-borg-repository&#34;&gt;Step 6: Remote Borg Repository&lt;/h4&gt;
&lt;p&gt;Take the scenario where the backups of many servers need to be maintained in a separate server. In this instance, a directory needs to be created for each of the systems that will be backed up. For this backup repository, create a folder named ‘backup’, and then within ‘backup’ a folder called ‘linode_01’. This folder will be initialized as a Borg repository.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#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;mkdir -p /mnt/backup/linode_01
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;borg init --encryption=repokey user@backup_server:/mnt/backup/linode_01&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The username, backup_server, repo can of course all be customized at the user’s discretion.&lt;/p&gt;
&lt;p&gt;While initialising the repo, a passphrase for each backup repository can be set for authentication.&lt;/p&gt;
&lt;h4 id=&#34;step-7-create-an-initial-backup-to-the-remote-borg-repository&#34;&gt;Step 7: Create an initial backup to the remote Borg repository&lt;/h4&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#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;borg create --stats ssh://user@backup_server/mnt/backup/linode_01::archive_1 /home/kannan/photos&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To enable the remote backups, the following three environment variables can be used to simplify the automation process:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#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;&lt;span style=&#34;color:#038&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;BORG_REPO&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;ssh://user@backup_server/mnt/backup/linode_01&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;BORG_PASSPHRASE&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;set_your_passpharase&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;BORG_RSH&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;ssh -i /home/kannan/.ssh/id_rsa_backups&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;With those environment variables set, the ‘borg create’ command can be shortened to the following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#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;borg create --stats ::archive_1 /home/kannan/photos&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;step-8-excluding-certain-directories-or-files&#34;&gt;Step 8: Excluding certain directories or files&lt;/h4&gt;
&lt;p&gt;In order to exclude certain directories or files, the create command has an &lt;code&gt;--exclude&lt;/code&gt; option or an exclude file/directory pattern can be generated. For example, the following command demonstrates how to exclude &lt;code&gt;/dev and /opt&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#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;borg create --stats ::archive_1 / --exclude /dev /opt&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;step-9-restoring-an-archive-through-extraction&#34;&gt;Step 9: Restoring an archive through extraction&lt;/h4&gt;
&lt;p&gt;The ‘borg extract’ command extracts the contents of an archive. As a preset default, the entire archive will be extracted. However, the extraction can be limited by passing the directory path or file path as arguments to the command. For example, this is how a single photo can be extracted from the Photos archive:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#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;borg extract ::archive_1 /home/kannan/photos/sunrise.jpg&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;step-10-pruning-older-backups&#34;&gt;Step 10: Pruning older backups&lt;/h4&gt;
&lt;p&gt;Every backup solution should have a way to maintain the older backups. Borg offers us &lt;code&gt;borg prune&lt;/code&gt; for this. It prunes a repository by deleting all archives not matching any of the specified retention options.&lt;/p&gt;
&lt;p&gt;For example, retain the final 10 archives from the day, another 6 end-of-week archives, and 3 of the end-of-month archive for every month using the following syntax:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#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;borg prune -v --list --keep-daily=&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;10&lt;/span&gt; --keep-weekly=&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;6&lt;/span&gt; --keep-monthly=&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;3&lt;/span&gt; ::&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Note that the double colons &lt;code&gt;::&lt;/code&gt; are required in order to automatically use the environment variables that were set prior.&lt;/p&gt;
&lt;p&gt;For more in-depth documentation on Borg backup, &lt;a href=&#34;https://borgbackup.readthedocs.io/en/stable/quickstart.html&#34;&gt;read the docs&lt;/a&gt;.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Rclone: upload to the cloud from your command line and much more</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2020/09/rclone-upload-to-cloud-from-cli/"/>
      <id>https://www.endpointdev.com/blog/2020/09/rclone-upload-to-cloud-from-cli/</id>
      <published>2020-09-09T00:00:00+00:00</published>
      <author>
        <name>Ardyn Majere</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2020/09/rclone-upload-to-cloud-from-cli/rclone_header_2_optimized.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;the-swiss-army-knife-of-storage&#34;&gt;The Swiss army knife of storage&lt;/h3&gt;
&lt;p&gt;Cloud storage providers like Google Drive are great solutions for storing files. You can upload your data and not worry about maintaining a separate system to host it, or all the security hassles that can bring. However, very few major cloud storage providers offer a command line interface or any other official way to upload without using their web interface or closed-source binary tools, if they even offer that.&lt;/p&gt;
&lt;p&gt;This obviously makes uploading files from servers difficult, but not impossible if you know the right tools.&lt;/p&gt;
&lt;p&gt;About a year ago Jon Jensen penned &lt;a href=&#34;/blog/2019/09/google-drive-for-vm-images/&#34;&gt;a blog post about gdrive&lt;/a&gt;, a Google Drive command-line tool. However, due to changes with Google’s Drive security, that tool no longer works. This led me to look for a replacement.&lt;/p&gt;
&lt;h3 id=&#34;our-use-case&#34;&gt;Our use case&lt;/h3&gt;
&lt;p&gt;Recently I had to put some large files in to long term storage on Google Drive, since we needed the local space back. We wanted to retain the data, but didn’t foresee needing to access it for some time, if ever. Google Drive was a good solution for us, but the problem became how to get it there.&lt;/p&gt;
&lt;p&gt;The files were too big, and some of them were not stored sparsely—​empty space was tacked on to the disk images. We wanted to encrypt them, as the drives potentially contained customer information. So we had to sequentially process the files, encrypt them, and upload them. I felt like this would take quite a bit of time.&lt;/p&gt;
&lt;p&gt;Enter &lt;a href=&#34;https://rclone.org/&#34;&gt;rclone&lt;/a&gt;. Rclone can connect to many different kinds of cloud storage providers, DIY cloud storage solutions, and even things like FTP and WebDAV. You can use rclone to copy files directly like rsync, or even use it to mount the remote storage as a local drive. We chose to do the latter.&lt;/p&gt;
&lt;p&gt;Rclone connects to a dizzying array of remote web services including Dropbox, Box, Amazon S3, Mega, SugarSync, and even homebrew cloud like ownCloud! This example uses Google Drive, but the instructions for many cloud providers are similar. The setup wizard can guide you through each step.&lt;/p&gt;
&lt;p&gt;We decided to mount the remote storage, then encrypt and compress the files with gpg outputting directly to the remote mount point. This would allow us to set several different keys for decryption. Rclone offers a crypt module for encrypting backups, but this requires keeping a password. We prefer each person who may need to decrypt this be able to use a key. This also allowed me to run the command and walk away rather than try to shepherd the process along manually.&lt;/p&gt;
&lt;p&gt;Once the rclone mount was in place, encrypting the files for upload was very simple. Having the storage mounted locally negated the need to encrypt it locally before uploading. We simply ran the following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#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;gpg --output /mnt/rclone/drive/sensitive_file.gpg --recipient recipient@email.com --encrypt ~/sensitive_file&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Does this sound useful? Well, if it does…&lt;/p&gt;
&lt;h3 id=&#34;how-to-do-this-yourself&#34;&gt;How to do this yourself&lt;/h3&gt;
&lt;p&gt;Installing rclone is as simple as downloading the binary from &lt;a href=&#34;https://rclone.org/&#34;&gt;rclone.org&lt;/a&gt; or cloning the Git repository and compiling it yourself. They also offer a script to automatically download the most recent version. Packages are also available in most operating system repositories, though as usual those tend to be a little older.&lt;/p&gt;
&lt;p&gt;Whichever method you choose, once you have rclone installed, you’ll need to configure it to use your remote storage.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ rclone config
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;2020/08/28 11:33:19 NOTICE: Config file &amp;#34;/home/user/.config/rclone/rclone.conf&amp;#34; not found - using defaults
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;No remotes found - make a new one
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;n) New remote
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;s) Set configuration password
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;q) Quit config
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;n/s/q&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Choose ‘n’ for a new remote, and name it something memorable.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;n/s/q&amp;gt; n
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;name&amp;gt; gdrive
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Type of storage to configure.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Choose a number from below, or type in your own value
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[snip]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;XX / Google Drive
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   \ &amp;#34;drive&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[snip]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Storage&amp;gt; drive&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;(At this point you could also choose a different storage type, and the rest of these instructions should still work.)&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;** See help for drive backend at: https://rclone.org/drive/ **
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Google Application Client Id
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Setting your own is recommended.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;See https://rclone.org/drive/#making-your-own-client-id for how to create your own.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;If you leave this blank, it will use an internal key which is low performance.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Enter a string value. Press Enter for the default (&amp;#34;&amp;#34;).
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;client_id&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;At this point, follow the instructions on how to &lt;a href=&#34;https://rclone.org/drive/#making-your-own-client-id&#34;&gt;make your own client id&lt;/a&gt; from rclone’s website or keep reading. If you leave this blank, the app will use a shared client id and there is global rate limiting in effect for every rclone instance.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;client_id&amp;gt; [snip].apps.googleusercontent.com
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Google Application Client Secret
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Setting your own is recommended.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Enter a string value. Press Enter for the default (&amp;#34;&amp;#34;).
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;client_secret&amp;gt; [snip]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Scope that rclone should use when requesting access from drive.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Enter a string value. Press Enter for the default (&amp;#34;&amp;#34;).
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Choose a number from below, or type in your own value
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; 1 / Full access all files, excluding Application Data Folder.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   \ &amp;#34;drive&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; 2 / Read-only access to file metadata and file contents.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   \ &amp;#34;drive.readonly&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   / Access to files created by rclone only.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; 3 | These are visible in the drive website.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   | File authorization is revoked when the user deauthorizes the app.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   \ &amp;#34;drive.file&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   / Allows read and write access to the Application Data folder.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; 4 | This is not visible in the drive website.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   \ &amp;#34;drive.appfolder&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   / Allows read-only access to file metadata but
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; 5 | does not allow any access to read or download file content.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   \ &amp;#34;drive.metadata.readonly&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;scope&amp;gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;At this point, choose a scope for the drive access. I’m not sure what the use cases are for the higher numbers, but 1–3 seem to be the most useful. For this demonstration, I chose 3—I want the app to be able to create new files but not read existing ones. For backups, I recommend 1 or 2.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ID of the root folder
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Leave blank normally.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Fill in to access ‘Computers’ folders (see docs), or for rclone to use
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;a non root folder as its starting point.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Note that if this is blank, the first time rclone runs it will fill it
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;in with the ID of the root folder.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Enter a string value. Press Enter for the default (&amp;#34;&amp;#34;).
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;root_folder_id&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can also jail the app to a specific subfolder. You’ll need to use a folder ID for this, rather than a folder name. See &lt;a href=&#34;https://rclone.org/drive/#root-folder-id&#34;&gt;the documentation&lt;/a&gt; if you need to do this.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Needed only if you want use SA instead of interactive login.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Enter a string value. Press Enter for the default (&amp;#34;&amp;#34;).
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;service_account_file&amp;gt;  # I left this blank
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Edit advanced config? (y/n)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;y) Yes
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;n) No (default)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;y/n&amp;gt; n
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Remote config
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Use auto config?
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; * Say Y if not sure
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; * Say N if you are working on a remote or headless machine
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;y) Yes (default)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;n) No&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;For our use case, we are setting this up on a remote machine, but you can use the interactive login if you have a graphical user interface on the machine in question.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;y/n&amp;gt; n
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Please go to the following link: https://accounts.google.com/o/oauth2/auth?access_type=offline&amp;amp;client_id=[snip]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Log in and authorize rclone for access
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Enter verification code&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;At this point copy the URL and follow the instructions on screen. Agree to the data sharing—If you have created your own API key, this data will be only shared with yourself, another reason to create your own.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Configure this as a team drive?
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;y) Yes
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;n) No (default)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;y/n&amp;gt; n
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;--------------------
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[gdrive]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;type = drive
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;client_id = [snip]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;client_secret = [snip]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;scope = drive.file
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;root_folder_id = rclone
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;token = [snip]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;--------------------
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;y) Yes this is OK (default)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;e) Edit this remote
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;d) Delete this remote
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;y/e/d&amp;gt; y
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Current remotes:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Name                 Type
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;====                 ====
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;gdrive               drive&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;At this point, you can quit the configuration mode, and your new rclone remote is ready to use!&lt;/p&gt;
&lt;h3 id=&#34;mounting-the-remote-drive-putting-it-all-together&#34;&gt;Mounting the remote drive (putting it all together)&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#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;$ sudo mkdir -p /mnt/rclone/drive
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ sudo chown -R &lt;span style=&#34;color:#369&#34;&gt;$USER&lt;/span&gt; /mnt/rclone/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ rclone mount gdrive: /mnt/rclone/drive/&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Note: If you wish to have the above command run in the background, add &amp;amp; to the end of the command. You’ll have to unmount it later with &lt;code&gt;fusermount -u /mnt/rclone/drive&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;At this point you can copy any files you like into the mount above. However, you could just do that with rclone directly: &lt;code&gt;rclone sourcefile gdrive:/.&lt;/code&gt; will work. Instead, I used the following command, which I showed at the very beginning of the blog post:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#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;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;for&lt;/span&gt; i in &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;$(&lt;/span&gt; ls /drives/source/directory &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;)&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  gpg --output /mnt/rclone/drive/&lt;span style=&#34;color:#369&#34;&gt;$i&lt;/span&gt;.gpg --recipient recipient@email.com --encrypt /drives/source/directory/&lt;span style=&#34;color:#369&#34;&gt;$i&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;done&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Quite a bit of setup. But the result is a mounted folder into which you can copy encrypted files directly, without any intermediary steps. The above command took a while, but by automating the task, the total work time was still less than it would have been trying to do it manually, especially given that we didn’t have the spare disk space to store a separate encrypted copy of our files.&lt;/p&gt;
&lt;p&gt;Want to do more? You could take a whole drive image with dd, run it through gpg, and pipe the output to the remote storage. You could use the crypt module for your own backups, rather than relying on gpg. The mount point acts as though it was a drive on your system, so any system utility like Timeshift could back up to the drive.&lt;/p&gt;
&lt;p&gt;If you have any other suggestions for how this might be used, please leave a comment!&lt;/p&gt;
&lt;h3 id=&#34;diy-google-api-key&#34;&gt;DIY Google API key&lt;/h3&gt;
&lt;p&gt;These instructions can also be found on the &lt;a href=&#34;https://rclone.org/drive/#making-your-own-client-id&#34;&gt;rclone website&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For this blog post I used my personal account and went through the steps below. Some of the steps may be different for a G Suite account. It’s a little cumbersome but will only take 5–10 minutes.&lt;/p&gt;
&lt;p&gt;Log in to the &lt;a href=&#34;https://console.developers.google.com/&#34;&gt;API console&lt;/a&gt; and create a new project.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/09/rclone-upload-to-cloud-from-cli/rclone_google_api_1-full.png&#34; alt=&#34;The Google APIs new project creation window&#34;&gt;&lt;/p&gt;
&lt;p&gt;Once you’ve created the project, click on ‘Enable APIs and Services’.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/09/rclone-upload-to-cloud-from-cli/rclone_google_api_2-full.png&#34; alt=&#34;The Google APIs dashboard&#34;&gt;&lt;/p&gt;
&lt;p&gt;Search for ‘Drive’ and click on the Google Drive API, which should be the first option.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/09/rclone-upload-to-cloud-from-cli/rclone_google_api_3-full.png&#34; alt=&#34;The Google API search&#34;&gt;&lt;/p&gt;
&lt;p&gt;Enable the Google Drive API.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/09/rclone-upload-to-cloud-from-cli/rclone_google_api_4-full.png&#34; alt=&#34;The Google Drive API welcome screen&#34;&gt;&lt;/p&gt;
&lt;p&gt;Once you have enabled this, do not click ‘Create Credentials’ directly — this takes you to the wrong wizard. Instead, click on Credentials in the left navigation pane, then ‘Create Credentials’, which gives you the option to just create OAuth credentials, which is what we need.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/09/rclone-upload-to-cloud-from-cli/rclone_google_api_5-full.png&#34; alt=&#34;The dashboard, showing how to navigate to the correct ‘Create Credentials’ screen&#34;&gt;&lt;/p&gt;
&lt;p&gt;Choose ‘OAuth credentials’ and fill in the OAuth consent screen if necessary. You can fill in limited information here, and just use ‘rclone’ for the name. If you are using a G Suite account you can choose ‘Internal’ rather than ‘External’.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/09/rclone-upload-to-cloud-from-cli/rclone_google_api_6-full.png&#34; alt=&#34;Filling in OAuth consent screen details.&#34;&gt;&lt;/p&gt;
&lt;p&gt;Once you have done that, create your OAuth ID. Again, you can just name this rclone. Choose the application type of ‘Desktop App’.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/09/rclone-upload-to-cloud-from-cli/rclone_google_api_7-full.png&#34; alt=&#34;Creating a new OAuth ID.&#34;&gt;&lt;/p&gt;
&lt;p&gt;You’re done! You can now use the OAuth ID displayed on this screen with rclone. Or go back and see it again from the dashboard under ‘Credentials’, then clicking the name of the OAuth ID you created.&lt;/p&gt;
&lt;p&gt;When creating the ID, it’ll say you will need to verify the app. This is only true if you plan on heavy use. You can use it quite easily without this step for smaller use cases.&lt;/p&gt;
&lt;p&gt;(These instructions were originally written by GitHub user @balazer, and are adapted here with many thanks.)&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Google Drive for virtual machine images</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2019/09/google-drive-for-vm-images/"/>
      <id>https://www.endpointdev.com/blog/2019/09/google-drive-for-vm-images/</id>
      <published>2019-09-30T00:00:00+00:00</published>
      <author>
        <name>Jon Jensen</name>
      </author>
      <content type="html">
        &lt;img src=&#34;/blog/2019/09/google-drive-for-vm-images/20190704-143912-sm.jpg&#34; alt=&#34;Pine Creek Pass, Teton Valley, Idaho&#34; /&gt;
&lt;!-- Photo by Jon Jensen --&gt;
&lt;p&gt;Recently we decommissioned an old database server. We wanted to keep a copy of its 53.7 GB virtual machine disk image in an archive in case there is ever any need to revive it. It is really unlikely that we will need it any time soon, or maybe ever, so we thought of putting it in one of the cloud storage services.&lt;/p&gt;
&lt;h3 id=&#34;cloud-storage&#34;&gt;Cloud storage&lt;/h3&gt;
&lt;p&gt;Cloud service pricing is often metered by storage, network, operations, and other fees, making it complicated to calculate what you will pay. We already use Amazon S3, Azure Storage, and &lt;a href=&#34;https://cloud.google.com/storage/&#34;&gt;Google Cloud Storage&lt;/a&gt; and they are all no exception. For our example 53.7 GB disk image, the Google Cloud Storage &lt;a href=&#34;https://cloud.google.com/storage/pricing&#34;&gt;Standard Storage pricing&lt;/a&gt; is currently:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;No charge for network ingress when we upload it&lt;/li&gt;
&lt;li&gt;About $1.08 to $1.40 USD per month to store the file in the US or EU&lt;/li&gt;
&lt;li&gt;About $6.50 each time we download the file to most places in the world!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These days a ~50 GB disk image is on the small side, so you can imagine the cost going up rapidly with larger disk sizes.&lt;/p&gt;
&lt;p&gt;Some cloud storage providers offer lower prices for slow-availability semi-offline storage, such as Amazon S3 Glacier or Google Coldline Storage. Those would indeed save us some money for monthly storage, but the most expensive aspect is the network egress cost, which is the same.&lt;/p&gt;
&lt;h3 id=&#34;local-storage&#34;&gt;Local storage&lt;/h3&gt;
&lt;p&gt;External USB disk drives have gotten very inexpensive in recent years, so we considered just spending roughly $180 for a 6 TB hard disk and storing our disk image there. We can fit over 100 VM images of this size on such a disk, so the cost works out to a one-time cost of about $1.80 each.&lt;/p&gt;
&lt;p&gt;But there are downsides to local storage, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It is a single point of failure. The disk can:
&lt;ul&gt;
&lt;li&gt;fail&lt;/li&gt;
&lt;li&gt;be lost or stolen&lt;/li&gt;
&lt;li&gt;be damaged by water, an impact such as someone dropping it or a horse kicking it 🐴😀, or a power surge or static electricity&lt;/li&gt;
&lt;li&gt;be forgotten if the employee who has it leaves and nobody remembers to take it over.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The data would likely be inaccessible while the employee is out of the office.&lt;/li&gt;
&lt;li&gt;Retrieval speed is limited by the upstream network bandwidth which is often fairly slow for consumer Internet connections and even crowded office networks.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For “just in case” old archival data, these risks are pretty reasonable and the price is good. But it would be nice to decouple the storage from any individual employee’s network speed.&lt;/p&gt;
&lt;h3 id=&#34;google-drive&#34;&gt;Google Drive&lt;/h3&gt;
&lt;p&gt;Then we remembered that we have Google Drive as part of G Suite, and even though we don’t normally use it this way, it offers a lot of space and could be a place to store infrequently-needed large archival data such as this.&lt;/p&gt;
&lt;p&gt;Many of us think of Google Drive primarily as its browser-based folder and file interface, a graphical consumer or business user service. So our instinct is to first download a file our desktop and then upload it from there to Google Drive using the browser.&lt;/p&gt;
&lt;p&gt;For our large, multi-gigabyte files on a server, that is cumbersome and slow, at least twice as slow as it should be. We really would prefer to upload them directly from the server to Google Drive. And to use the old archived images, we would want to download them from Drive directly to a remote server, not first to a desktop.&lt;/p&gt;
&lt;h4 id=&#34;uploading&#34;&gt;Uploading&lt;/h4&gt;
&lt;p&gt;Conveniently for us, Google Drive has a &lt;a href=&#34;https://developers.google.com/drive/&#34;&gt;web service API&lt;/a&gt; we can use! &lt;a href=&#34;http://olivermarshall.net/how-to-upload-a-file-to-google-drive-from-the-command-line/&#34;&gt;Oliver Marshall’s nice article&lt;/a&gt; shows how to use the open source &lt;a href=&#34;https://github.com/gdrive-org/gdrive&#34;&gt;gdrive command-line tool&lt;/a&gt; to access it.&lt;/p&gt;
&lt;p&gt;The easiest thing to do after we have gdrive authenticate with our Google account is:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;gdrive upload /path/to/file&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;which will put it in the base folder of My Drive.&lt;/p&gt;
&lt;p&gt;You can also use &lt;code&gt;gdrive list …&lt;/code&gt; to find a specific folder you would like to upload it into.&lt;/p&gt;
&lt;p&gt;To give an idea of the speed, I had several files to upload from a Linux server, and measured speed of about 12 MB/sec. for each of 3 parallel uploads on a 100 Mbps server connection.&lt;/p&gt;
&lt;h4 id=&#34;verifying&#34;&gt;Verifying&lt;/h4&gt;
&lt;p&gt;Did the huge files make it without error? Google Drive doesn’t show them until they’re complete, which is good, so we will not see any partial files. To confirm, let’s check file size and MD5 hash value, which isn’t visible in the Drive web interface, but is via the API, along with the view &amp;amp; download URLs:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# gdrive info 1YMEoEWWOvuBGSk0GJBv7c0E4cSEOoc6n
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Id: 1YMEoEWWOvuBGSk0GJBv7c0E4cSEOoc6n
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Name: prod-db-20190731
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Path: VM images/Database server/prod-db-20190731
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Mime: application/octet-stream
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Size: 53.7 GB
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Created: 2019-08-08 21:20:50
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Modified: 2019-08-08 21:20:50
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Md5sum: 43f7e727047cb2bbbfb54b413752c229
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Shared: True
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Parents: 1HBkixF0UxraH6nHyD1Fiwx5X5k7dfaKm
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ViewUrl: https://drive.google.com/a/your.domain/file/d/1YMEoEWWOvuBGSk0GJBv7c0E4cSEOoc6n/view?usp=drivesdk
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;DownloadUrl: https://drive.google.com/a/your.domain/uc?id=1YMEoEWWOvuBGSk0GJBv7c0E4cSEOoc6n&amp;amp;export=download&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;When we run &lt;code&gt;md5sum&lt;/code&gt; (or &lt;code&gt;md5&lt;/code&gt; on BSD systems such as macOS) on the original file or block device on our server, the resulting hash value should match the Md5sum value gdrive shows.&lt;/p&gt;
&lt;h4 id=&#34;cautions&#34;&gt;Cautions&lt;/h4&gt;
&lt;p&gt;Regular Google Drive accounts are tied to the user, so if you leave the organization and they remove your account, your files will need to be deleted or reassigned to another user.&lt;/p&gt;
&lt;p&gt;Use a Shared Drive (formerly called Team Drive) to avoid having the files tied to a specific user. There is even an &lt;a href=&#34;https://developers.google.com/drive/api/v3/search-shareddrives&#34;&gt;API for G Suite admins&lt;/a&gt; to search all shared drives in their account.&lt;/p&gt;
&lt;p&gt;I haven’t seen anything to make me think this API is any less reliable than anything in Google Cloud Platform, but it certainly is not promoted as being something to use for a busy production web application. It seems safest to use it for archival storage like this that will be accessed rarely, by a human.&lt;/p&gt;
&lt;p&gt;Once authorized, your gdrive command-line tool has access to all your Google Drive files. That isn’t something you want to leave sitting around for ill-intentioned people to perhaps access. So when you’re done uploading your files, remove the app authorization until the next time you need it. In your browser go to: Google Drive &amp;gt; Settings (gear icon) &amp;gt; Manage Apps &amp;gt; GDrive &amp;gt; Options &amp;gt; Remove app.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Data Center Relocation Initiatives: Daunting But Achievable</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2018/12/data_center_relocation_initiatives_daunting_but_achievable/"/>
      <id>https://www.endpointdev.com/blog/2018/12/data_center_relocation_initiatives_daunting_but_achievable/</id>
      <published>2018-12-20T00:00:00+00:00</published>
      <author>
        <name>Charles Chang</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2018/12/data_center_relocation_initiatives_daunting_but_achievable/banner.jpg&#34; alt=&#34;Datacenter room&#34;&gt;
&lt;a href=&#34;https://www.flickr.com/photos/seeweb/9771315606/&#34;&gt;Photo by Seeweb · CC BY-SA 2.0, cropped&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&#34;introduction&#34;&gt;Introduction&lt;/h3&gt;
&lt;p&gt;A customer asked for our help dealing with logistical nightmares they encountered during a hardware update and data center relocation project. The customer had two active data centers, and wanted to relocate one of them to a modern Tier 4 facility to improve its performance and provide redundancy for critical systems. They also wanted to consolidate or decommission equipment to reduce recurring expenses and reduce their carbon footprint. The client decided to move to a Tier 4 data center because they provide across-​the-​board redundancy within the data center.&lt;/p&gt;
&lt;h3 id=&#34;moving-forward-with-tier-iv&#34;&gt;Moving forward with Tier IV&lt;/h3&gt;
&lt;p&gt;
&lt;img align=&#34;right&#34; src=&#34;/blog/2018/12/data_center_relocation_initiatives_daunting_but_achievable/data-center-1.jpg&#34; style=&#34;margin: 1em&#34; width=&#34;200&#34;&gt;
Enterprises typically transition to Tier 4 data centers because they offer the highest uptime guarantees, and have no single points of failure. These facilities are fully redundant in terms of electrical circuits, cooling, and networking. This architecture is best able to withstand even the most serious technical incidents without server availability being affected. Tier IV facilities have contracts with disaster management companies who will provide them, for example, with fuel in the event that a natural disaster damages the power grid.
&lt;/p&gt;
&lt;p&gt;(More information about the four data center levels is at the end of this post.)&lt;/p&gt;
&lt;h3 id=&#34;key-issues&#34;&gt;Key Issues&lt;/h3&gt;
&lt;p&gt;Some of the customer’s key concerns were:&lt;/p&gt;
&lt;p&gt;1. To safely move their virtual environment to the new data center.&lt;/p&gt;
&lt;p&gt;2. To protect assets during the relocation.&lt;/p&gt;
&lt;p&gt;3. To completely shut down the current data center and migrate seamlessly to the new data center.&lt;/p&gt;
&lt;p&gt;4. To update external DNS records without causing any downtime for the web applications used by their customers.&lt;/p&gt;
&lt;h3 id=&#34;project-planning&#34;&gt;Project Planning&lt;/h3&gt;
&lt;p&gt;To manage data center relocations is challenging because there are many moving parts and variables. Typically, planning starts 6 months ahead of the scheduled relocation date. We seek to understand the role of each system by consulting in-depth with all the teams involved. We analyze each part of the client’s tech infrastructure, break it down, and strategize about the best approach for handling the migration of each component. Additional time to plan can help especially when a large group of stakeholders is involved. Successful planning requires hard and sometimes extensive work, but makes an otherwise overwhelming data center migration smooth and worry free.&lt;/p&gt;
&lt;p&gt;Here are some of our procedures for pre-​migration preparation:&lt;/p&gt;
&lt;p&gt;1. Process the paperwork for new hardware. Identify, review, and approve all applications. Hire movers. Schedule and coordinate with internal management and key stakeholders like IT staff, system admins, contractors, circuit providers, etc.&lt;img align=&#34;right&#34; src=&#34;/blog/2018/12/data_center_relocation_initiatives_daunting_but_achievable/data-center-2.jpg&#34; style=&#34;margin: 1em&#34; width=&#34;200&#34;/&gt;&lt;/p&gt;
&lt;p&gt;2. Collect and analyze resource usage associated with the critical and non-critical systems and compare resource needs to resource availability at the new facility.&lt;/p&gt;
&lt;p&gt;3. Design and schedule the migration of critical systems to the new data center. Discuss the plan with key stakeholders.&lt;/p&gt;
&lt;p&gt;4. Make sure replicas of critical systems are up to date.&lt;/p&gt;
&lt;p&gt;5. Set up a new environment with new hardware in the new data center and establish network connections.&lt;/p&gt;
&lt;p&gt;6. Prepare hardware and virtualization technology in the new data center.&lt;/p&gt;
&lt;p&gt;7. Test, retest, and burn in the new hardware (2–4 weeks of testing).&lt;/p&gt;
&lt;p&gt;8. Create a non-​essential server within the virtualization environment and test it.&lt;/p&gt;
&lt;h3 id=&#34;a-challenge-eliminating-downtime&#34;&gt;A Challenge: Eliminating Downtime&lt;/h3&gt;
&lt;img align=&#34;right&#34; src=&#34;/blog/2018/12/data_center_relocation_initiatives_daunting_but_achievable/data-center-3.jpg&#34; style=&#34;margin: 1em&#34; width=&#34;200&#34;/&gt;
&lt;p&gt;To minimize or eliminate downtime requires very careful preparation. With this particular client, we had the luxury of their already having a secondary data center, which could accommodate a majority of the critical systems during the migration.&lt;/p&gt;
&lt;p&gt;We originally planned to leverage this by simply changing the external DNS records to point to this data center during the migration, but during planning determined that the external DNS record would not replicate worldwide quickly enough to meet the uptime needs of their global operation.&lt;/p&gt;
&lt;h3 id=&#34;solutions&#34;&gt;Solutions&lt;/h3&gt;
&lt;p&gt;After discussing various approaches, we ended up using a load balancer. A load balancer is a network device that routes incoming traffic destined for a single destination (web site, application, or service) and ‘shares’ the incoming connections across multiple destination devices or services. In this case, the data center we were to migrate to housed the client’s critical systems like web servers, database, etc. The secondary data center housed a replica of the critical systems using a replication technology, e.g. Actifio, Rubrik, SAN mirroring, VMWare SRM, or Zerto — we used Actifio in this scenario.
&lt;img align=&#34;right&#34; src=&#34;/blog/2018/12/data_center_relocation_initiatives_daunting_but_achievable/data-center-4.jpg&#34; style=&#34;margin: 1em&#34; width=&#34;200&#34;/&gt;&lt;/p&gt;
&lt;p&gt;The customer had multiple systems with external DNS records and public IP addresses. We did not change the public DNS names for the critical systems. We did change the public IP addresses associated with the public DNS names, to point to the load balancer, which then pointed to the appropriate data center throughout the process. This setup enabled a gradual, flexible, and fully controlled transition to the new data center.&lt;/p&gt;
&lt;h3 id=&#34;summary&#34;&gt;Summary&lt;/h3&gt;
&lt;img align=&#34;right&#34; src=&#34;/blog/2018/12/data_center_relocation_initiatives_daunting_but_achievable/data-center-5.jpg&#34; style=&#34;margin: 1em&#34; width=&#34;200&#34;/&gt;
&lt;p&gt;Our client wanted to reduce their servers’ power usage 40% by consolidating into a hyper-​converged system. Other goals were to reduce floor space by 50%, and monthly recurring data center charges by 50%. We transitioned them to a new hardware stack (compute, storage, network, backup environment) in a Tier 4 data center, and saved them 50% percent on data center operation costs. Additionally, during the process we corrected issues associated with disaster recovery and business continuity, leaving the client’s operations more resilient, affordable, and more secure.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;appendix-data-center-classifications&#34;&gt;Appendix: Data Center Classifications&lt;/h3&gt;
&lt;h4 id=&#34;tier-iv-best&#34;&gt;Tier IV (Best)&lt;/h4&gt;
&lt;p&gt;Tier IV data centers have redundancies for every process and data protection stream. No single outage or error can shut down the system. To keep Tier IV ranking, data centers must have 99.995% minimum uptime per year. They come equipped with 2N+1 infrastructure (two times the amount required for operation plus a backup), which is considered “fully redundant.”&lt;/p&gt;
&lt;h4 id=&#34;tier-iii-average&#34;&gt;Tier III (Average)&lt;/h4&gt;
&lt;p&gt;N+1 (the amount required for operation plus a backup) fault tolerance, which means a Tier III facility can undergo routine maintenance without a hiccup in operations. 99.982% minimum uptime per annum, used for maintenance and overwhelming emergencies.&lt;/p&gt;
&lt;h4 id=&#34;tier-ii-minimal&#34;&gt;Tier II (Minimal)&lt;/h4&gt;
&lt;p&gt;99.749% minimum uptime. Tier II facilities have considerably more downtime than Tier III facilities, primarily because Tier II facilities have less redundancy. They do however have partial cooling redundancy and multiple power redundancies.&lt;/p&gt;
&lt;h4 id=&#34;tier-i-basic-cheapest&#34;&gt;Tier I (Basic, Cheapest)&lt;/h4&gt;
&lt;p&gt;99.671% minimum uptime, with 28.8 hours of downtime each year.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Half day GlusterFS training in Selangor, Malaysia</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2017/04/half-day-glusterfs-training-in-selangor/"/>
      <id>https://www.endpointdev.com/blog/2017/04/half-day-glusterfs-training-in-selangor/</id>
      <published>2017-04-02T00:00:00+00:00</published>
      <author>
        <name>Muhammad Najmi bin Ahmad Zabidi</name>
      </author>
      <content type="html">
        &lt;p&gt;On January 21, 2017, I had an opportunity to join a community-organized training on storage focused on GlusterFS. GlusterFS is an open source cloud-based filesharing network. The training was not a strictly structured training as the topic approached knowledge sharing from various experts and introduced GlusterFS to the ones who were new to it. The first session was delivered by Mr Adzmely Mansor from NexoPrima. He shared a bit of his view on GlusterFS and technologies that are related to it.&lt;/p&gt;
&lt;p&gt;Mr Haris, a freelance Linux expert, later led a GlusterFS technical class. Here we created two virtual machines (we used Virtualbox) to understand how GlusterFS works in a hands-on scenario. We used Ubuntu 16.04 as the guest OS during technical training. We used Digital Ocean’s GlusterFS settings as a base of reference. The below commands detail roughly what we did during the training.&lt;/p&gt;
&lt;p&gt;In GlusterFS the data section is called as “brick”. Hence we could have a lot of “bricks” if we have it more than once :) . As Ubuntu already had the related packages in its repository, we could simply run apt-get for the package installation. Our class notes were loosely based from Digital Ocean’s GlusterFS article &lt;a href=&#34;https://www.digitalocean.com/community/tutorials/how-to-create-a-redundant-storage-pool-using-glusterfs-on-ubuntu-servers&#34;&gt;here&lt;/a&gt;. (Note: the article was based on Ubuntu 12.04 so some of the steps could be omitted).&lt;/p&gt;
&lt;p&gt;The GlusterFS packages could be installed as a superuser with the following command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;apt-get install glusterfs-server&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Since we were using a bridged VM during the demo, we simply edited the /etc/hosts in the each VM so they could communicate between each other by using hostname instead of using typing the IP manually.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;root@gluster2:~# grep gluster /etc/hosts
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;192.168.1.11 gluster1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;127.0.0.1 gluster2&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here we will try to probe the remote host whether it is reachable:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;root@gluster2:~# gluster peer probe gluster1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;peer probe: success. Host gluster1 port 24007 already in peer list&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The following commands create the storage volume. Later, whatever we put in the /data partition will be reachable on the other gluster node.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;gluster volume create datastore1 replica 2 transport tcp gluster1:/data gluster2:/data
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;gluster volume create datastore1 replica 2 transport tcp gluster1:/data gluster2:/data force
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;gluster volume start datastore1&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Most of the parts here could be retrieved from the link that I gave above. But let’s see what will happen later on when the mounting part is done.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cd /datastore1/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;root@gluster2:/datastore1# touch blog
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;root@gluster2:/datastore1# ls -lth
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;total 512
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;-rw-r--r-- 1 root root  0 Mar 14 21:33 blog
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;-rw-r--r-- 1 root root 28 Jan 21 12:15 ujian.txt&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The same output could be retrieved from gluster1&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;root@gluster1:/datastore1# ls -lth
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;total 512
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;-rw-r--r-- 1 root root  0 Mar 14 21:33 blog
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;-rw-r--r-- 1 root root 28 Jan 21 12:15 ujian.txt&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&#34;/blog/2017/04/half-day-glusterfs-training-in-selangor/image-0-big.jpeg&#34; imageanchor=&#34;1&#34;&gt;&lt;img border=&#34;0&#34; height=&#34;240&#34; src=&#34;/blog/2017/04/half-day-glusterfs-training-in-selangor/image-0.jpeg&#34; width=&#34;320&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Mr. Adzmely gave explanation on the overall picture of GlusterFS&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/blog/2017/04/half-day-glusterfs-training-in-selangor/image-1-big.jpeg&#34; imageanchor=&#34;1&#34;&gt;&lt;img border=&#34;0&#34; height=&#34;240&#34; src=&#34;/blog/2017/04/half-day-glusterfs-training-in-selangor/image-1.jpeg&#34; width=&#34;320&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Mr. Haris explained on the technical implementation of GlusterFS&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In terms of the application, the redundancy based storage is good for situations where you have a file being updated on several servers and you need to ensure the file is there for retrieval even if one of the servers is down. One audience member shared his experience deploying GlusterFS in his workplace (a university) for the purpose of new intake of student’s registration. If anyone ever uses Samba filesystem or NFS, this kind of similar, but GlusterFS is much more advanced. I recommend additional reading &lt;a href=&#34;http://www.linux-mag.com/id/7833/&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Handling databases in dev environments for web development</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2015/04/handling-databases-in-dev-environments/"/>
      <id>https://www.endpointdev.com/blog/2015/04/handling-databases-in-dev-environments/</id>
      <published>2015-04-21T00:00:00+00:00</published>
      <author>
        <name>Spencer Christensen</name>
      </author>
      <content type="html">
        &lt;p&gt;One of the biggest problems for web development environments is copying large amounts of data.  Every time a new environment is needed, all that data needs to be copied.  Source code should be tracked in version control software, and so copying it should be a simple matter of checking it out from the code repository.  So that is usually not the problem.  The main problem area is database data.  This can be very large, take a long time to copy, and can impact the computers involved in the copy (usually the destination computer gets hammered with IO which makes load go high).&lt;/p&gt;
&lt;p&gt;Often databases for development environments are created by copying a database dump from production and then importing that database dump.  And since database dumps are text, they can be highly compressed, which can result in a relatively small file to copy over.  But the import of the dump can still take lots of time and cause high load on the dev computer as it rebuilds tables and indexes.  As long as your data is relatively small, this process may be perfectly acceptable.&lt;/p&gt;
&lt;h3 id=&#34;your-database-will-get-bigger&#34;&gt;Your database WILL get bigger&lt;/h3&gt;
&lt;p&gt;At some point though your database will get so big that this process will take too long and cause too much load to be acceptable.&lt;/p&gt;
&lt;p&gt;To address the problem you can try to reduce the amount of data involved by only dumping a portion of the database data instead of all of it, or possibly using some “dummy sample data” instead.  These techniques may work if you don’t care that development environments no longer have the same data as production.  However, one serious problem with this is that a bug or behavior found in production can’t be replicated in a development environment because the data involved isn’t the same.  For example, say a customer can’t checkout on the live site but you can’t replicate the bug in your development environment to fix the bug.  In this example, the root cause of the problem could be a bug in the code handling certain products that are out of stock, and since the dev database didn’t have the same data it could make finding and fixing these types of problems &lt;em&gt;a lot harder&lt;/em&gt;.&lt;/p&gt;
&lt;h3 id=&#34;snapshots&#34;&gt;Snapshots&lt;/h3&gt;
&lt;p&gt;Another option is to use file system snapshots, like LVM snapshots, to quickly make clones of the database without needing to import the database dump each time.  This works great if development environments live on the same server, or at least the development databases live on the same server.  You would need to create a volume to hold a single copy of the database; this copy would be the origin for all snapshots.  Then for each development environment, you could snapshot the origin volume, mount it read-write in a place accessible by the developer, customize the database configuration (like setting a unique port number to listen on), and then start up the database.  This then provides a clone of the entire database in a tiny fraction of the time and uses less disk space and other system resources too.&lt;/p&gt;
&lt;p&gt;In using snapshots there are some things you’ll need to be careful about.  Snapshots are usually created using copy-on-write tables.  The more snapshots mounted read-write, the more IO overhead is involved for the volumes involved.  For this reason it is important that writes to the origin volume be avoided as much as possible while the snapshots are open.  Also, snapshots that get a lot of writes can fill up their copy-on-write table, and depending on the file system and database that you are using this can be a big problem.  So it is important to monitor each open snapshot for how full it is and increase their size if needed so they don’t fill up.  Updating the origin database will require shutting down and removing all snapshots first, then update the origin database, then create and mount all the snapshots again.  This is because all the copy-on-write tables would get full if you tried to update the origin while the snapshots are open.&lt;/p&gt;
&lt;p&gt;Using snapshots like this may sound more complicated, and it is, but the processes involved can be scripted and automated and the benefits can be pretty significant if you have several developers and a lot of data to copy.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Shrink XFS partition: Almost possible with LVM</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2015/01/shrink-xfs-partition-almost-possible/"/>
      <id>https://www.endpointdev.com/blog/2015/01/shrink-xfs-partition-almost-possible/</id>
      <published>2015-01-29T00:00:00+00:00</published>
      <author>
        <name>Emanuele “Lele” Calò</name>
      </author>
      <content type="html">
        &lt;p&gt;If you happen to have reached this page because you’re trying to shrink an XFS filesystem let’s put things straight: sorry, that’s not possible.&lt;/p&gt;
&lt;p&gt;But before you go away you should know there’s still hope and I’d like to show you how I used a little workaround to avoid reinstalling a Red Hat Enterprise Linux 7 or CentOS 7 VM using XFS dump/restore on-the-fly and LVM capabilities, both standard choices for the regular RHEL/CentOS 7 setup.&lt;/p&gt;
&lt;p&gt;First of all let’s clarify the situation I found myself in. For various reasons I had a CentOS 7 VM with everything already configured and working, installed not many days ago to test new software we’re evaluating.&lt;/p&gt;
&lt;p&gt;The VM itself is hosted on a dedicated server we manage on our own, so I had a certain degree of freedom to what I could do without the need of paying any additional fee. You may not be in this same situation, but you can probably try some similar solution for little money if you’re using an “hourly-billed” VPS provider.&lt;/p&gt;
&lt;p&gt;The problem was that, even if everything was working and configured, the virtual hard disk device attached to the machine was &lt;em&gt;too big&lt;/em&gt; and &lt;em&gt;on the wrong storage area of the virtualization hosting server&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;There was also another minor glitch: The VM itself was using an old virtualization device driver (IDE-based) instead of the new VIRTIO one. Since I knew that the virtualized OS CentOS 7 is capable of using VIRTIO based devices I also took the chance to fix this.&lt;/p&gt;
&lt;p&gt;Unfortunately, XFS is not capable of being shrunk at the moment (and for the foreseeable future) so what I needed to do was to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;add a new virtual device correctly using VIRTIO and stored in the right storage area of the virtualization host server&lt;/li&gt;
&lt;li&gt;migrate all the filesystems to the new virtual device&lt;/li&gt;
&lt;li&gt;set the VM OS to be working from the newly-built partition&lt;/li&gt;
&lt;li&gt;dismiss the old virtual device&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In my specific case this translated to connect to the virtualization hosting server, create a new LVM logical volume to host the virtual disk device for the VM and then add the new virtual device to the VM configuration. Unfortunately in order to have the VM see the new virtual device I had to shut it down.&lt;/p&gt;
&lt;p&gt;While connected to the virtualization host server I also downloaded and attached to the VM the latest ISO of &lt;a href=&#34;http://www.system-rescue-cd.org/&#34;&gt;SysRescueCD&lt;/a&gt; which is a data rescue specialized Linux distribution. I’m specifically using this distro since it’s one of the few which offers the XFS dump/restore tools on the live ISO.&lt;/p&gt;
&lt;p&gt;Now the VM was ready to be booted with the SysRescueCD Live OS and then I could start working my way through all the needed fixes. If you’re doing something similar, of course please make sure you have &lt;strong&gt;offsite backups&lt;/strong&gt; and have double-checked that they’re readable before doing anything else.&lt;/p&gt;
&lt;p&gt;First of all inspect your &lt;em&gt;dmesg&lt;/em&gt; output and find what is the source virtual device and what’s the new target virtual device. In my case the source was /dev/sda and the target was /dev/vda&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#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;dmesg | less&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then create a partition on the new device (eg: /dev/vda1) as Linux type for the /boot partition; this should be of the same size as the source /boot partition (eg: /dev/sda1) and dedicate all the remaining space to a new LVM type partition (eg: /dev/vda2)&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#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;fdisk /dev/vda
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;# [create /boot and LVM partitions]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You could also mount and copy the /boot files or re-create them entirely if you need to change the /boot partition size. Since I kept /boot exactly the same so I could use &lt;em&gt;ddrescue&lt;/em&gt; (a more verbose version of classic Unix &lt;em&gt;dd&lt;/em&gt;).&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#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;ddrescue /dev/sda1 /dev/vda1&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The next step is supposed to migrate the MBR and should be working but in my case the boot phase kept failing so I also needed to reinstall the bootloader via the CentOS 7 rescue system (not covered in this tutorial but briefly mentioned near the end of the article).&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#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;ddrescue -i0 -s512 /dev/sda /dev/vda&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then create the target LVM volumes.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#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;pvcreate /dev/vda2
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;vgcreate fixed_centos_VG /dev/vda2
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;lvcreate -L 1G -n swap fixed_centos_VG
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;lvcreate -l 100%FREE -n root fixed_centos_VG
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;vgchange -a y fixed_centos_VG&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Create the target XFS filesystem.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#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;mkfs.xfs -L root /dev/fixed_centos_VG/root&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And then create the swap partition.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#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;mkfs.swap /dev/fixed_centos_VG/swap&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Next create the needed mountpoints and mount the old source and the new empty filesystems.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#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;mkdir /mnt/disk_{wrong,fixed}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mount /dev/fixed_centos_VG/root /mnt/disk_fixed
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;vgchange -a y wrong_centos_VG
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mount /dev/centos_VG/root /mnt/disk_wrong&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now here’s the real XFS magic. We’ll use &lt;strong&gt;xfsdump&lt;/strong&gt; and &lt;strong&gt;xfsrestore&lt;/strong&gt; to copy the filesystem content (files, directory, special files) without having to care about files permission, type, extended ACLs or anything else. Plus since it’s only moving &lt;em&gt;the content&lt;/em&gt; of the filesystem we won’t need to have a partition of the same size and it won’t take as long as copying the entire block device as the process will just have to go through real used space.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#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;xfs_dump -J - /mnt/disk_wrong | xfs_restore -J - /mnt/disk_fixed&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you want a more verbose output, leave out the &lt;em&gt;-J&lt;/em&gt; option. After the process is done, be sure to &lt;strong&gt;carefully verify&lt;/strong&gt; that everything is in place in the new partition.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#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;ls -lhtra /mnt/disk_fixed/&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then unmount the disks and deactivate the LVM VGs.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#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;umount /mnt/disk_{old,fixed}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;vgchange -a n centos_VG
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;vgchange -a n fixed_centos_VG&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;At this point in order to avoid changing anything inside the virtualized OS (fstab, grub and so on), let’s remove the old VG and rename the newer one with the same name the old one had.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#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;vgremove centos_VG
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pvremove /dev/sda2
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;vgrename {fixed_,}centos_VG&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You should be now able to shutdown the VM again, detach the old disk and start the new VM which will be using the new smaller virtual device.&lt;/p&gt;
&lt;p&gt;If the boot phase keeps failing, boot the CentOS installation media in &lt;strong&gt;rescue&lt;/strong&gt; mode and after &lt;strong&gt;chroot&lt;/strong&gt;-ing inside your installation run &lt;strong&gt;grub-install /dev/vda&lt;/strong&gt; (targeting your new main device) to reinstall grub.&lt;/p&gt;
&lt;p&gt;Only after everything is working as expected, proceed to remove the old unneeded device and remove it from the virtualization host server.&lt;/p&gt;

      </content>
    </entry>
  
</feed>
