<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Alejandro Akbal</title>
    <description>The latest articles on DEV Community by Alejandro Akbal (@alejandroakbal).</description>
    <link>https://dev.to/alejandroakbal</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F287309%2Fcb6409a4-498f-4eef-8d00-f83ed3b757ee.png</url>
      <title>DEV Community: Alejandro Akbal</title>
      <link>https://dev.to/alejandroakbal</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/alejandroakbal"/>
    <language>en</language>
    <item>
      <title>How to preload images for canvas in JavaScript</title>
      <dc:creator>Alejandro Akbal</dc:creator>
      <pubDate>Tue, 17 Jan 2023 11:26:51 +0000</pubDate>
      <link>https://dev.to/alejandroakbal/how-to-preload-images-for-canvas-in-javascript-251c</link>
      <guid>https://dev.to/alejandroakbal/how-to-preload-images-for-canvas-in-javascript-251c</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;This past week, I was toying around with the HTML5 canvas element, trying to join two images together.&lt;br&gt;
At first, it seemed fine, but when I tried to reload the website, it was a mess. One image would load, but the other wouldn't.&lt;/p&gt;

&lt;p&gt;Investigating, I found out that the images were being loaded asynchronously.&lt;br&gt;
But the JavaScript code was running anyway, without waiting for the images, ending up with a messed up canvas.&lt;/p&gt;

&lt;p&gt;That is why I decided to write this tutorial, to help you &lt;strong&gt;preload images for canvas in modern JavaScript&lt;/strong&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  Preload images
&lt;/h2&gt;

&lt;p&gt;With the help of the &lt;code&gt;Promise.all()&lt;/code&gt; function, we can devise a solution to preload images.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * @param {string[]} urls - Array of Image URLs
 * @returns {Promise&amp;lt;HTMLImageElement[]&amp;gt;} - Promise that resolves when all images are loaded, or rejects if any image fails to load
 */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;preloadImages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;urls&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;promises&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;urls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

      &lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onerror&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Image failed to load: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;promises&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can then use it like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;urls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://example.com/image1.png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://example.com/image2.png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="c1"&gt;// Important to use `await` here&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;images&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;preloadImages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;urls&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Can also be destructured&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;image1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;image2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;preloadImages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;urls&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// For example:&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;canvas&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2d&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;drawImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;images&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;drawImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;images&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  End
&lt;/h2&gt;

&lt;p&gt;That was it.&lt;br&gt;
It's a small one-time setup on your project, that works like a charm.&lt;/p&gt;

&lt;h3&gt;
  
  
  Self-promotion
&lt;/h3&gt;

&lt;p&gt;If you have found this useful, then you should follow me, I will be posting more interesting content! 🥰&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/github"&gt;GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/twitter"&gt;Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/dev.to"&gt;Dev.to&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Congratulations, today you have learned how to preload images for canvas in modern JavaScript. Without needing to complicate your code or use libraries.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Let me know if the tutorial was useful to you in the comments!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>beginners</category>
      <category>javascript</category>
      <category>canvas</category>
    </item>
    <item>
      <title>How to create a test database with Laravel Sail</title>
      <dc:creator>Alejandro Akbal</dc:creator>
      <pubDate>Sun, 21 Aug 2022 17:16:13 +0000</pubDate>
      <link>https://dev.to/alejandroakbal/how-to-create-a-test-database-with-laravel-sail-528d</link>
      <guid>https://dev.to/alejandroakbal/how-to-create-a-test-database-with-laravel-sail-528d</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;I have worked on projects that required the production and testing database to be the same.&lt;/p&gt;

&lt;p&gt;This could be because some features work in MariaDB, but not in SQLite. Or some bugs appear in MySQL, but not in PostgreSQL.&lt;/p&gt;

&lt;p&gt;When you're working in a large project, &lt;strong&gt;you need to know that the testing database works exactly like the one in production&lt;/strong&gt;.&lt;br&gt;
You can't allow a bug to appear in production because the test database was different.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: If the project is small, chances are you are fine using SQLite for testing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Today, I'm going to guide you through setting up a testing database in your current database server, thanks to Laravel Sail.&lt;/p&gt;


&lt;h2&gt;
  
  
  Before we start
&lt;/h2&gt;

&lt;p&gt;This tutorial uses PostgreSQL as the database.&lt;br&gt;
But the idea is the same for any other database: &lt;strong&gt;create a separate database in your current database server&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Preface
&lt;/h3&gt;
&lt;h3&gt;
  
  
  Requirements
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Laravel 9&lt;/li&gt;
&lt;li&gt;Laravel Sail&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Database setup
&lt;/h2&gt;

&lt;p&gt;We will use the database server declared in the &lt;code&gt;docker-compose.yml&lt;/code&gt; file.&lt;br&gt;
In this case, PostgreSQL.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pgsql&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;postgres:14'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, enter PostgreSQL's CLI&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sail psql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note: If you are using MySQL, you can use &lt;code&gt;sail mysql&lt;/code&gt; instead.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now, create a new database&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This database is separated from the default database that your application uses.&lt;/p&gt;




&lt;h2&gt;
  
  
  PHPUnit configuration
&lt;/h2&gt;

&lt;p&gt;Now, modify the &lt;code&gt;phpunit.xml&lt;/code&gt; file to use the new database and database server.&lt;br&gt;
Should end up similar to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;php&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;env&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"DB_CONNECTION"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"pgsql"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;env&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"DB_DATABASE"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"testing"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/php&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Run the tests
&lt;/h2&gt;

&lt;p&gt;Now, you can run the tests and see if they work.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sail &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--parallel&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It should look similar to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   PASS  Tests&lt;span class="se"&gt;\U&lt;/span&gt;nit&lt;span class="se"&gt;\E&lt;/span&gt;xampleTest
  ✓ example

  Tests:  1 passed
  Time:   0.7s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  End
&lt;/h2&gt;

&lt;p&gt;That was it.&lt;br&gt;
It's a one-time setup, that works like a charm.&lt;/p&gt;

&lt;h3&gt;
  
  
  Self-promotion
&lt;/h3&gt;

&lt;p&gt;If you have found this useful, then you should follow me, I will be posting more interesting content! 🥰&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/github"&gt;GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/twitter"&gt;Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/dev.to"&gt;Dev.to&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Congratulations, today you have learned how to create a test database in Laravel Sail.&lt;br&gt;
Without needing to modify the &lt;code&gt;docker-compose.yml&lt;/code&gt; file or creating more services.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Let me know if the tutorial was useful to you in the comments!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>beginners</category>
      <category>laravel</category>
      <category>docker</category>
    </item>
    <item>
      <title>How to build a free plan in Laravel Spark</title>
      <dc:creator>Alejandro Akbal</dc:creator>
      <pubDate>Sun, 10 Oct 2021 11:20:23 +0000</pubDate>
      <link>https://dev.to/alejandroakbal/how-to-build-a-free-plan-in-laravel-spark-41c5</link>
      <guid>https://dev.to/alejandroakbal/how-to-build-a-free-plan-in-laravel-spark-41c5</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;I'm going to explain how to build a free plan for your Laravel Spark Next application.&lt;/p&gt;

&lt;p&gt;I will be using Paddle as the payment gateway, but the steps are almost identical with Stripe.&lt;/p&gt;

&lt;p&gt;Best of all? No credit card required for the free plan.&lt;/p&gt;




&lt;h2&gt;
  
  
  Before we start
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Preface
&lt;/h3&gt;

&lt;p&gt;You should know that Laravel Spark is built on top of Laravel Cashier, so know that the process is very similar with both.&lt;/p&gt;

&lt;h3&gt;
  
  
  Requirements
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Laravel 8 with Spark Next&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Laravel Spark installation
&lt;/h2&gt;

&lt;p&gt;Before starting the tutorial, you should have followed the &lt;a href="https://spark.laravel.com/docs/1.x/installation.html"&gt;official installation guide&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Laravel Spark plan configuration
&lt;/h2&gt;

&lt;p&gt;Let's imagine that we are building a new application which offers a free plan and a paid plan.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;"Free" plan&lt;/strong&gt; will have a limit of &lt;strong&gt;1 project&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;"Paid" plan&lt;/strong&gt; will have a limit of &lt;strong&gt;5 projects&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We need to configure both plans in the &lt;code&gt;config/spark.php&lt;/code&gt; file, like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;

  &lt;span class="s1"&gt;'plans'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="s1"&gt;'name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Free'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="s1"&gt;'short_description'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'This is the free plan'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="c1"&gt;// Random _(not used by other plans)_ ID&lt;/span&gt;
          &lt;span class="s1"&gt;'monthly_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="s1"&gt;'features'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
              &lt;span class="s1"&gt;'1 Project'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;],&lt;/span&gt;
          &lt;span class="s1"&gt;'options'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
              &lt;span class="s1"&gt;'projects'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;],&lt;/span&gt;
          &lt;span class="c1"&gt;// IMPORTANT&lt;/span&gt;
          &lt;span class="s1"&gt;'archived'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="s1"&gt;'name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Paid'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="s1"&gt;'short_description'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'This is the paid plan'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="s1"&gt;'monthly_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;999990&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="s1"&gt;'yearly_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;999991&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="s1"&gt;'yearly_incentive'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Save 20%!'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="s1"&gt;'features'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
              &lt;span class="s1"&gt;'5 Projects'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;],&lt;/span&gt;
          &lt;span class="s1"&gt;'options'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
              &lt;span class="s1"&gt;'projects'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;],&lt;/span&gt;
          &lt;span class="s1"&gt;'archived'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;// ...&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, the limits are actually set in the &lt;code&gt;options&lt;/code&gt; array.&lt;/p&gt;

&lt;p&gt;It's important that the "Free" plan is set to &lt;code&gt;archived&lt;/code&gt; to prevent users from selecting that plan on the billing page.&lt;/p&gt;

&lt;p&gt;The "Free" plan has to have a random &lt;code&gt;monthly_id&lt;/code&gt;, so the plan can be used internally.&lt;br&gt;
&lt;em&gt;It doesn't need to be a working product ID, nor do you have to create it on Paddle/Stripe&lt;/em&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  Modify User model
&lt;/h2&gt;

&lt;p&gt;Now that we have created a "Free" plan, we need to modify the &lt;code&gt;User&lt;/code&gt; model to add a &lt;code&gt;getPlan&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;This method will get the current users plan, &lt;strong&gt;or the "Free" plan if the user has no active plan&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getPlan&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$plan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;sparkPlan&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$plan&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$plan&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// Fallback to "Free" plan&lt;/span&gt;
        &lt;span class="nv"&gt;$plan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Spark&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;plans&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'user'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;firstWhere&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'='&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Free'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$plan&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// ...&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: As mentioned in the steps before, you need to add a &lt;code&gt;monthly_id&lt;/code&gt; to the "Free" plan, otherwise it will be skipped by the &lt;code&gt;Spark::plans('user')&lt;/code&gt; call and will not be found.&lt;br&gt;
The ID can be random, don't worry.&lt;/p&gt;


&lt;h2&gt;
  
  
  Getting the User's plan
&lt;/h2&gt;

&lt;p&gt;Through your application you can access a user plan and limits like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nv"&gt;$plan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getPlan&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nv"&gt;$plan&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// "Free"&lt;/span&gt;

&lt;span class="nv"&gt;$plan&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'projects'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; &lt;span class="c1"&gt;// 1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  End
&lt;/h2&gt;

&lt;p&gt;As you can see, what we actually do is fallback to the "Free" plan if the user has no active plan.&lt;/p&gt;

&lt;p&gt;And this plan doesn't actually go through the payment process, so you don't need to ask the users with a credit card.&lt;/p&gt;

&lt;h3&gt;
  
  
  Self-promotion
&lt;/h3&gt;

&lt;p&gt;If you have found this useful, then you should follow me, I will be posting more interesting content! 🥰&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/github"&gt;GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/twitter"&gt;Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/dev.to"&gt;Dev.to&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Or support me financially. 💸&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/github/sponsor"&gt;GitHub Sponsors&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/liberapay"&gt;LiberaPay&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/paypal"&gt;PayPal&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Congratulations, today you have learned how to build a free plan in Laravel Spark.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Let me know if the tutorial was useful to you in the comments!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>laravel</category>
      <category>stripe</category>
      <category>paddle</category>
    </item>
    <item>
      <title>How to roll back a Dokku deployment</title>
      <dc:creator>Alejandro Akbal</dc:creator>
      <pubDate>Sun, 25 Jul 2021 08:50:03 +0000</pubDate>
      <link>https://dev.to/alejandroakbal/how-to-roll-back-a-dokku-deployment-5825</link>
      <guid>https://dev.to/alejandroakbal/how-to-roll-back-a-dokku-deployment-5825</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;Sometimes you end up deploying an application to Dokku and then realize that you want to revert the changes you made.&lt;/p&gt;

&lt;p&gt;In this tutorial we'll go over how to roll back a Dokku deployment.&lt;/p&gt;




&lt;h2&gt;
  
  
  Before we start
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Preface
&lt;/h3&gt;

&lt;p&gt;Keep in mind that rolling back a deployment is a dangerous operation, proceed with caution.&lt;/p&gt;

&lt;h3&gt;
  
  
  Requirements
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Dokku server&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Don't have a Dokku server?&lt;br&gt;
Check out my &lt;a href="https://blog.akbal.dev/create-your-own-heroku-with-dokku-on-digitalocean"&gt;Dokku tutorial&lt;/a&gt;.&lt;/p&gt;


&lt;h3&gt;
  
  
  Get the commit hash
&lt;/h3&gt;

&lt;p&gt;First well need to get the hash of the commit that we want to roll back to.&lt;/p&gt;

&lt;p&gt;To accomplish that, list out the last 10 commits that have been made to the repository.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git log &lt;span class="nt"&gt;--pretty&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;format:&lt;span class="s2"&gt;"%h - %s"&lt;/span&gt; &lt;span class="nt"&gt;-10&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will get an output similar to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;3cacb03 - Revert &lt;span class="s2"&gt;"build: broaden possible purged files"&lt;/span&gt;
25e3b2b - chore: move node dependency to dev dependencies
6a42416 - Revert &lt;span class="s2"&gt;"ci: run npm "&lt;/span&gt;build&lt;span class="s2"&gt;" script in predeploy stage"&lt;/span&gt;
0b53fdd - ci: execute php buildpack first
2d27d60 - ci: run npm &lt;span class="s2"&gt;"build"&lt;/span&gt; script &lt;span class="k"&gt;in &lt;/span&gt;predeploy stage
1bc1276 - build: broaden possible purged files
1bed300 - style: lint
5ab255c - Revert &lt;span class="s2"&gt;"build: only run tailwind JIT mode on local"&lt;/span&gt;
23b0c4b - build: fix data-tables styles getting purged
52ca32e - ci: move scripts back to app.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now copy the hash of the commit that you want to roll back to.&lt;br&gt;
For example &lt;code&gt;2d27d60&lt;/code&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  How to rollback
&lt;/h2&gt;

&lt;p&gt;Now that we have the commit hash, we can roll back to it.&lt;/p&gt;

&lt;p&gt;Just force push to Dokku with the commit hash, instead of the local branch.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# git push --force &amp;lt;remote&amp;gt; &amp;lt;local branch&amp;gt;:&amp;lt;remote branch&amp;gt;&lt;/span&gt;
git push &lt;span class="nt"&gt;--force&lt;/span&gt; dokku de7fc85:master
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;That is it!&lt;/strong&gt;&lt;br&gt;
Now Dokku will build the application from that commit.&lt;br&gt;
Effectively rolling back to that commit.&lt;/p&gt;




&lt;h2&gt;
  
  
  End
&lt;/h2&gt;

&lt;p&gt;That was easy, wasn't it?&lt;/p&gt;

&lt;h3&gt;
  
  
  Self-promotion
&lt;/h3&gt;

&lt;p&gt;If you have found this useful, then you should follow me, I will be posting more interesting content! 🥰&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/github"&gt;GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/twitter"&gt;Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/dev.to"&gt;Dev.to&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Or support me financially. 💸&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/github/sponsor"&gt;GitHub Sponsors&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/liberapay"&gt;LiberaPay&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/paypal"&gt;PayPal&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Congratulations, today you have learned how to roll back a Dokku deployment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Let me know if the tutorial was useful to you in the comments!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>beginners</category>
      <category>docker</category>
    </item>
    <item>
      <title>How to deploy git submodules to CapRover</title>
      <dc:creator>Alejandro Akbal</dc:creator>
      <pubDate>Mon, 14 Jun 2021 14:57:57 +0000</pubDate>
      <link>https://dev.to/alejandroakbal/how-to-deploy-git-submodules-to-caprover-74</link>
      <guid>https://dev.to/alejandroakbal/how-to-deploy-git-submodules-to-caprover-74</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;In this tutorial I will explain how to get &lt;code&gt;git submodules&lt;/code&gt; to deploy correctly to &lt;code&gt;CapRover&lt;/code&gt; using the &lt;code&gt;CapRover CLI&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Before we start
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Preface
&lt;/h3&gt;

&lt;p&gt;Having some knowledge about &lt;code&gt;CapRover&lt;/code&gt;, &lt;code&gt;Docker&lt;/code&gt; and &lt;code&gt;Git&lt;/code&gt; will help you understand how this solution works.&lt;/p&gt;




&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;When you use &lt;code&gt;caprover deploy&lt;/code&gt;, what happens underneath is that the CLI uses &lt;code&gt;git archive&lt;/code&gt; to make a compressed &lt;code&gt;tar&lt;/code&gt; of your repository. It then sends and deploys that file to your CapRover server.&lt;/p&gt;

&lt;p&gt;But there are some problems with &lt;code&gt;git archive&lt;/code&gt;:&lt;br&gt;
&lt;strong&gt;It does NOT include the &lt;code&gt;.git&lt;/code&gt; directory in the &lt;code&gt;tar&lt;/code&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;So what you end up deploying is not really a git repository...&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;And if you were using &lt;code&gt;git submodules&lt;/code&gt; in your repository, they are not downloaded, since the &lt;code&gt;.git&lt;/code&gt; directory is missing.&lt;/p&gt;

&lt;p&gt;To solve that issue, I have found a solution that is separated into three steps.&lt;/p&gt;


&lt;h2&gt;
  
  
  First step: Create a Dockerfile
&lt;/h2&gt;

&lt;p&gt;The first step to use &lt;code&gt;git submodules&lt;/code&gt; in CapRover is to create a &lt;code&gt;Dockerfile&lt;/code&gt; and download the &lt;code&gt;git submodules&lt;/code&gt; as a build step.&lt;/p&gt;

&lt;p&gt;You will need to create a &lt;code&gt;captain-definition&lt;/code&gt; file and point it to a &lt;code&gt;Dockerfile&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"schemaVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dockerfilePath"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./Dockerfile"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, you will need to create a &lt;code&gt;Dockerfile&lt;/code&gt; that contains the following build step.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;RUN &lt;/span&gt;git submodule update &lt;span class="nt"&gt;--init&lt;/span&gt; &lt;span class="nt"&gt;--recursive&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:15-alpine&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;apk &lt;span class="nt"&gt;--no-cache&lt;/span&gt; add git

&lt;span class="c"&gt;# IMPORTANT: Download git submodules&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;git submodule update &lt;span class="nt"&gt;--init&lt;/span&gt; &lt;span class="nt"&gt;--recursive&lt;/span&gt;

&lt;span class="c"&gt;# ...&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", "src/main"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Second step: Include .git directory in the tar
&lt;/h2&gt;

&lt;p&gt;The second step is to improve what &lt;code&gt;caprover deploy&lt;/code&gt; does.&lt;br&gt;
Create a &lt;code&gt;tar&lt;/code&gt; file of your repository, while adding the &lt;code&gt;.git&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;For that, you can use the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Archive git repository&lt;/span&gt;
git archive HEAD &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; deploy.tar

&lt;span class="c"&gt;# Add `.git` directory to `tar`&lt;/span&gt;
&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; deploy.tar .git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Third step: Deploy the tar
&lt;/h2&gt;

&lt;p&gt;Now that you have both the &lt;code&gt;tar&lt;/code&gt; with the &lt;code&gt;.git&lt;/code&gt; directory, and a &lt;code&gt;Dockerfile&lt;/code&gt; that downloads the &lt;code&gt;git submodules&lt;/code&gt;, you are ready to deploy.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Deploy the `tar` to your CapRover server&lt;/span&gt;
npx caprover deploy &lt;span class="nt"&gt;-t&lt;/span&gt; ./deploy.tar

&lt;span class="c"&gt;# Remove the tar&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; ./deploy.tar
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  End
&lt;/h2&gt;

&lt;p&gt;That was it, I hope you had luck and your deployment was successful!&lt;/p&gt;

&lt;p&gt;Feel free to use the following script to perform all of these steps automatically.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="c"&gt;# Archive git repository&lt;/span&gt;
git archive HEAD &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; deploy.tar

&lt;span class="c"&gt;# Add `.git` directory to `tar`&lt;/span&gt;
&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; deploy.tar .git

&lt;span class="c"&gt;# Deploy the `tar` to your CapRover server&lt;/span&gt;
npx caprover deploy &lt;span class="nt"&gt;-t&lt;/span&gt; ./deploy.tar

&lt;span class="c"&gt;# Remove the tar&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; ./deploy.tar

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Self-promotion
&lt;/h3&gt;

&lt;p&gt;If you have found this useful, then you should follow me, I will be posting more interesting content! 🥰&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/github"&gt;GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/twitter"&gt;Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/dev.to"&gt;Dev.to&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Or support me financially. 💸&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/github/sponsor"&gt;GitHub Sponsors&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/liberapay"&gt;LiberaPay&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/paypal"&gt;PayPal&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Congratulations, today you have learned how to deploy &lt;code&gt;git submodules&lt;/code&gt; to your &lt;code&gt;CapRover&lt;/code&gt; server.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Let me know if the tutorial was useful to you in the comments!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>docker</category>
      <category>caprover</category>
    </item>
    <item>
      <title>Earn money with the AdGuard Affiliate Program</title>
      <dc:creator>Alejandro Akbal</dc:creator>
      <pubDate>Fri, 30 Apr 2021 14:15:01 +0000</pubDate>
      <link>https://dev.to/alejandroakbal/earn-money-with-the-adguard-affiliate-program-4ha6</link>
      <guid>https://dev.to/alejandroakbal/earn-money-with-the-adguard-affiliate-program-4ha6</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;Chances are that you want to monetize your audience, and you have learned about the &lt;strong&gt;&lt;a href="https://aff.adguard.com/?ref=AlejandroAkbal" rel="noopener noreferrer"&gt;50% lifetime commissions on the AdGuard Affiliate Program&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So let's learn everything about the affiliate / referral program.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;When you become an AdGuard affiliate, you will earn 50% of the purchases that your referred users make.&lt;/p&gt;

&lt;p&gt;The process looks like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You share your referral link to your audience&lt;/li&gt;
&lt;li&gt;A user installs AdGuard using your link&lt;/li&gt;
&lt;li&gt;The user makes a purchase and activates the program with it&lt;/li&gt;
&lt;li&gt;At the moment of activation, you will earn 50% of the revenue&lt;/li&gt;
&lt;li&gt;You will also earn 50% from the following renewals&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It does not matter when and how a user purchases the license key.&lt;br&gt;
&lt;strong&gt;The important thing is that a user has installed AdGuard with your link.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Register
&lt;/h2&gt;

&lt;p&gt;To start the process you will have to go to the &lt;a href="https://aff.adguard.com/?ref=AlejandroAkbal" rel="noopener noreferrer"&gt;AdGuard Affiliate Program website&lt;/a&gt; and create an account.&lt;/p&gt;

&lt;p&gt;It is very easy, just fill the form with your details.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffnlzm7sftswv3ci3zycr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffnlzm7sftswv3ci3zycr.png" alt="Registration form"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Set a payment method
&lt;/h2&gt;

&lt;p&gt;Now that you have an account you will have to go to the &lt;a href="https://aff.adguard.com/en/member/profile.html" rel="noopener noreferrer"&gt;Profile page&lt;/a&gt; and set a payment method to receive the money.&lt;/p&gt;

&lt;p&gt;You can choose between PayPal, Visa, MasterCard, Webmoney and QIWI.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6gz83q0afr1c29cy0nkm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6gz83q0afr1c29cy0nkm.png" alt="User profile"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Share
&lt;/h2&gt;

&lt;p&gt;To start sharing, you will have to get your affiliate link.&lt;/p&gt;

&lt;p&gt;Go to the &lt;a href="https://aff.adguard.com/en/member/promo.html" rel="noopener noreferrer"&gt;Marketing tools page&lt;/a&gt;, click on “Your website links” and copy the link.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgh1apiod71btljcj220u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgh1apiod71btljcj220u.png" alt="AdGuard Affiliate link"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now comes the “hard work”, sharing the link with your user base.&lt;/p&gt;

&lt;p&gt;How you choose to share it, is entirely up to you.&lt;/p&gt;

&lt;p&gt;For example, you could share it to your &lt;strong&gt;Twitter followers, create a review of the AdGuard App, or mention it in your YouTube videos&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;There are many ways to do it, good luck!&lt;/p&gt;




&lt;h2&gt;
  
  
  Get paid
&lt;/h2&gt;

&lt;p&gt;Now that you have hopefully made some money... It's time to receive it!&lt;/p&gt;

&lt;p&gt;To cash out it's as easy as going to the &lt;a href="https://aff.adguard.com/en/member/payments.html" rel="noopener noreferrer"&gt;Payouts page&lt;/a&gt; and &lt;strong&gt;ordering a payout&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Remember that the minimum balance for cashing out is $20.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  End
&lt;/h2&gt;

&lt;p&gt;That was it, now do your magic and earn that affiliate money!&lt;/p&gt;

&lt;h3&gt;
  
  
  Self-promotion
&lt;/h3&gt;

&lt;p&gt;If you have found this useful, then you should follow me, I will be posting more interesting content! 🥰&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/github" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/twitter" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/dev.to"&gt;Dev.to&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Or support me financially. 💸&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/github/sponsor" rel="noopener noreferrer"&gt;GitHub Sponsors&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/liberapay" rel="noopener noreferrer"&gt;LiberaPay&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/paypal" rel="noopener noreferrer"&gt;PayPal&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Congratulations, today you have learned how to join the AdGuard Affiliate Program and hopefully make some easy money.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Let me know if this guide was useful to you in the comments!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>adguard</category>
      <category>affiliate</category>
      <category>referral</category>
    </item>
    <item>
      <title>How to detect and update to the latest version with Nuxt PWA</title>
      <dc:creator>Alejandro Akbal</dc:creator>
      <pubDate>Thu, 11 Mar 2021 16:05:56 +0000</pubDate>
      <link>https://dev.to/alejandroakbal/how-to-detect-and-update-to-the-latest-version-with-nuxt-pwa-1845</link>
      <guid>https://dev.to/alejandroakbal/how-to-detect-and-update-to-the-latest-version-with-nuxt-pwa-1845</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;I was working on one of my Nuxt projects and noticed that some users were using old versions, which was causing some errors to pop up.&lt;/p&gt;

&lt;p&gt;I investigated and learned that sometimes PWAs don't update if the user doesn't manually refresh the website. So...&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Let's learn how to automatically update to the latest PWA version.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Before we start
&lt;/h2&gt;

&lt;p&gt;This is a simple tutorial for projects with Nuxt and the PWA module, nothing else is required.&lt;/p&gt;

&lt;h3&gt;
  
  
  Requirements
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Nuxt&lt;/li&gt;
&lt;li&gt;Nuxt PWA module&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Create a new plugin
&lt;/h2&gt;

&lt;p&gt;To start, you will need to go to your plugins directory and create a new JavaScript file. I will name it &lt;code&gt;pwa-update.js&lt;/code&gt; but feel free to use whatever you want to.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// pwa-update.js&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;workbox&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$workbox&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;workbox&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Workbox couldn't be loaded.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;workbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;installed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isUpdate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;The PWA is on the latest version.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;There is an update for the PWA, reloading...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reload&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Add the plugin to the Nuxt config
&lt;/h2&gt;

&lt;p&gt;Then we have to add it to the plugins array on &lt;code&gt;nuxt.config.js&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// nuxt.config.js&lt;/span&gt;

&lt;span class="c1"&gt;// ...&lt;/span&gt;

  &lt;span class="nx"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;~/plugins/pwa-update.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;client&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;

&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  End
&lt;/h2&gt;

&lt;p&gt;And that was it. Easy right?&lt;/p&gt;

&lt;h3&gt;
  
  
  Self-promotion
&lt;/h3&gt;

&lt;p&gt;If you have found this useful, then you should follow me, I will be posting more interesting content! 🥰&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/github"&gt;GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/twitter"&gt;Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/dev.to"&gt;Dev.to&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/medium"&gt;Medium&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/linkedin"&gt;LinkedIn&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Or support me financially. 💸&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/github/sponsor"&gt;GitHub Sponsors&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/liberapay"&gt;LiberaPay&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/paypal"&gt;PayPal&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Congratulations, today you have set up automatic PWA updates for your project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Let me know if this tutorial was useful to you in the comments!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>vue</category>
      <category>nuxt</category>
      <category>pwa</category>
    </item>
    <item>
      <title>How to set up Matomo analytics in your Hashnode blog</title>
      <dc:creator>Alejandro Akbal</dc:creator>
      <pubDate>Sat, 16 Jan 2021 21:51:18 +0000</pubDate>
      <link>https://dev.to/alejandroakbal/how-to-set-up-matomo-analytics-in-your-hashnode-blog-24e0</link>
      <guid>https://dev.to/alejandroakbal/how-to-set-up-matomo-analytics-in-your-hashnode-blog-24e0</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;Today I bring incredible news; Hashnode has finally added &lt;a href="https://hashnode.com/post/support-for-matomo-formerly-piwik-ckevg1tnn00tcn1s18ima1ihs"&gt;support for Matomo analytics&lt;/a&gt;!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Matomo are powerful open source web analytics which gives you 100% data ownership.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I just noticed it in the &lt;em&gt;integrations&lt;/em&gt; tab of Hashnode's dashboard, so...&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Let's learn how to integrate Matomo in Hashnode!&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Before we start
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Requirements
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;A Hashnode blog&lt;/li&gt;
&lt;li&gt;A Matomo server&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  New Matomo website
&lt;/h2&gt;

&lt;p&gt;To start, you will need to go to your Matomo server and create a new website.&lt;/p&gt;

&lt;p&gt;Create it in: &lt;code&gt;All websites&lt;/code&gt; &amp;gt; &lt;code&gt;Add a new website&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now, fill the form with your current Hashnode details, grab the site ID, and you are ready to go!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2UmUdysU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/qob56vcg9hgq74mx9x64.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2UmUdysU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/qob56vcg9hgq74mx9x64.png" alt="Matomo form"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Add Matomo integration
&lt;/h2&gt;

&lt;p&gt;Now that you have the Matomo site set up, go to your Hashnode dashboard and choose the &lt;code&gt;integrations&lt;/code&gt; tab.&lt;/p&gt;

&lt;p&gt;There you will see a form for Matomo. Just fill it with your Matomo server details.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ri4BEdgn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/cxixcmxqqla761cmrnka.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ri4BEdgn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/cxixcmxqqla761cmrnka.png" alt="Hashnode form"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  End
&lt;/h2&gt;

&lt;p&gt;And that was it, &lt;strong&gt;100% open source analytics on your Hashnode blog in less than 5 minutes&lt;/strong&gt;. Easy right?&lt;/p&gt;

&lt;h3&gt;
  
  
  What's next?
&lt;/h3&gt;

&lt;p&gt;Take a look at your Matomo analytics, you should start to see people coming in to your blog to enjoy your incredible articles. 😉&lt;/p&gt;

&lt;h3&gt;
  
  
  Self-promotion
&lt;/h3&gt;

&lt;p&gt;If you have found this useful then you should follow me, I will be posting more interesting content! 🥰&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/github"&gt;GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/twitter"&gt;Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/dev.to"&gt;Dev.to&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/medium"&gt;Medium&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/linkedin"&gt;LinkedIn&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Or support me financially. 💸&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/github/sponsor"&gt;GitHub Sponsors&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/liberapay"&gt;LiberaPay&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/paypal"&gt;PayPal&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Congratulations, today you have set up Matomo analytics for your Hashnode blog in a breeze.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Let me know if the tutorial was useful to you in the comments!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>beginners</category>
      <category>hashnode</category>
      <category>matomo</category>
    </item>
    <item>
      <title>How to set up automatic updates on Ubuntu server</title>
      <dc:creator>Alejandro Akbal</dc:creator>
      <pubDate>Fri, 15 Jan 2021 14:00:34 +0000</pubDate>
      <link>https://dev.to/alejandroakbal/how-to-set-up-automatic-updates-on-ubuntu-server-53jf</link>
      <guid>https://dev.to/alejandroakbal/how-to-set-up-automatic-updates-on-ubuntu-server-53jf</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;Setting up automatic updates can be a daunting task. But fear not, this tutorial will help you set up automatic updates correctly in less than 10 minutes.&lt;/p&gt;




&lt;h2&gt;
  
  
  Before we start
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Preface
&lt;/h3&gt;

&lt;p&gt;While this tutorial is focused on &lt;strong&gt;Ubuntu Server&lt;/strong&gt;, it can be used for many other distributions that use the same package manager, like Ubuntu Desktop, Debian, Linux Mint, etc.&lt;/p&gt;

&lt;h3&gt;
  
  
  Requirements
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;An Ubuntu server&lt;/li&gt;
&lt;li&gt;An internet connection&lt;/li&gt;
&lt;li&gt;Access to your server&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Update
&lt;/h2&gt;

&lt;p&gt;First you'll have to update to the latest package repository definition.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Install
&lt;/h2&gt;

&lt;p&gt;Then, we will need to install the package that does all the magic for us, &lt;a href="https://wiki.debian.org/UnattendedUpgrades"&gt;unattended-upgrades&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; unattended-upgrades
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Chances are that you already have this package installed.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Configuration
&lt;/h2&gt;

&lt;p&gt;Next step is to configure the package, lets start by opening the configuration file in the &lt;code&gt;nano&lt;/code&gt; text editor.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/apt/apt.conf.d/50unattended-upgrades
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ...&lt;/span&gt;

&lt;span class="nc"&gt;Unattended&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nc"&gt;Upgrade&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nc"&gt;Allowed&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nc"&gt;Origins&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"${distro_id}:${distro_codename}"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="s2"&gt;"${distro_id}:${distro_codename}-security"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="c1"&gt;// Extended Security Maintenance; doesn't necessarily exist for&lt;/span&gt;
        &lt;span class="c1"&gt;// every release and this system may not have it installed, but if&lt;/span&gt;
        &lt;span class="c1"&gt;// available, the policy for updates is such that unattended-upgrades&lt;/span&gt;
        &lt;span class="c1"&gt;// should also install from here by default.&lt;/span&gt;
        &lt;span class="s2"&gt;"${distro_id}ESMApps:${distro_codename}-apps-security"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="s2"&gt;"${distro_id}ESM:${distro_codename}-infra-security"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;//      "${distro_id}:${distro_codename}-updates";&lt;/span&gt;
&lt;span class="c1"&gt;//      "${distro_id}:${distro_codename}-proposed";&lt;/span&gt;
&lt;span class="c1"&gt;//      "${distro_id}:${distro_codename}-backports";&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should read the configuration file to understand what it is doing, don't worry if you don't understand most things.&lt;/p&gt;

&lt;p&gt;The important step is to uncomment the following lines.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Required, updates common software&lt;/span&gt;
&lt;span class="s2"&gt;"${distro_id}:${distro_codename}-updates"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Optional, removes unused packages when updating&lt;/span&gt;
&lt;span class="nc"&gt;Unattended&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nc"&gt;Upgrade&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nc"&gt;Remove&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nc"&gt;Unused&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nc"&gt;Kernel&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nc"&gt;Packages&lt;/span&gt; &lt;span class="s2"&gt;"true"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nc"&gt;Unattended&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nc"&gt;Upgrade&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nc"&gt;Remove&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nc"&gt;Unused&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nc"&gt;Dependencies&lt;/span&gt; &lt;span class="s2"&gt;"true"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Optional, reboot automatically the system if needed at certain time&lt;/span&gt;
&lt;span class="nc"&gt;Unattended&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nc"&gt;Upgrade&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nc"&gt;Automatic&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nc"&gt;Reboot&lt;/span&gt; &lt;span class="s2"&gt;"true"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nc"&gt;Unattended&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nc"&gt;Upgrade&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nc"&gt;Automatic&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nc"&gt;Reboot&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nc"&gt;Time&lt;/span&gt; &lt;span class="s2"&gt;"02:00"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;You can search the file with &lt;code&gt;ctrl&lt;/code&gt; + &lt;code&gt;w&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then exit &lt;code&gt;nano&lt;/code&gt; with &lt;code&gt;ctrl&lt;/code&gt; + &lt;code&gt;x&lt;/code&gt; and press &lt;code&gt;Y&lt;/code&gt; to save modifications.&lt;/p&gt;




&lt;h2&gt;
  
  
  Enable
&lt;/h2&gt;

&lt;p&gt;Now that everything is set up, lets enable the automatic updates. For this, you will need to configure one last file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/apt/apt.conf.d/20auto-upgrades
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The file might be empty, in that case, just paste the following.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="no"&gt;APT&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nc"&gt;Periodic&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nc"&gt;Update&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nc"&gt;Package&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nc"&gt;Lists&lt;/span&gt; &lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="no"&gt;APT&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nc"&gt;Periodic&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nc"&gt;Download&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nc"&gt;Upgradeable&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nc"&gt;Packages&lt;/span&gt; &lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="no"&gt;APT&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nc"&gt;Periodic&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nc"&gt;Unattended&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nc"&gt;Upgrade&lt;/span&gt; &lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="no"&gt;APT&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nc"&gt;Periodic&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nc"&gt;AutocleanInterval&lt;/span&gt; &lt;span class="s2"&gt;"7"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The values are specified in days, so auto-clean will happen every week and auto-updates every day.&lt;/p&gt;




&lt;h2&gt;
  
  
  Test
&lt;/h2&gt;

&lt;p&gt;Let's test if everything is set up correctly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;unattended-upgrades &lt;span class="nt"&gt;--dry-run&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will run &lt;code&gt;unattended-upgrades&lt;/code&gt; without making any real change, making sure everything is correctly set up.&lt;/p&gt;




&lt;h2&gt;
  
  
  End
&lt;/h2&gt;

&lt;p&gt;That was it, easy right?&lt;/p&gt;

&lt;h3&gt;
  
  
  What's next?
&lt;/h3&gt;

&lt;p&gt;You might want to &lt;a href="https://blog.akbal.dev/how-to-free-up-space-on-ubuntu-server"&gt;free up space on your server&lt;/a&gt; after all the updates are done!&lt;/p&gt;

&lt;h3&gt;
  
  
  Self-promotion
&lt;/h3&gt;

&lt;p&gt;If you have found this useful then you should follow me, I will be posting more interesting content! 🥰&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/github"&gt;GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/twitter"&gt;Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/dev.to"&gt;Dev.to&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/medium"&gt;Medium&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/linkedin"&gt;LinkedIn&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Or support me financially. 💸&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/github/sponsor"&gt;GitHub Sponsors&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/liberapay"&gt;LiberaPay&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/paypal"&gt;PayPal&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Congratulations, today you have learned how to set up and configure automatic updates on your Ubuntu server thanks to the &lt;code&gt;unattended-upgrades&lt;/code&gt; package.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Let me know if the tutorial was useful to you in the comments!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>ubuntu</category>
      <category>devops</category>
      <category>cloud</category>
    </item>
    <item>
      <title>How to remove unused Docker resources</title>
      <dc:creator>Alejandro Akbal</dc:creator>
      <pubDate>Wed, 16 Dec 2020 15:38:52 +0000</pubDate>
      <link>https://dev.to/alejandroakbal/how-to-remove-unused-docker-resources-514o</link>
      <guid>https://dev.to/alejandroakbal/how-to-remove-unused-docker-resources-514o</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;Chances are that you've been running Docker for some time and found out that your system's storage is almost full.&lt;/p&gt;

&lt;p&gt;This is completely normal as Docker bundles all the needed dependencies with each container and doesn't remove anything if you don't explicitly tell it to do so.&lt;/p&gt;

&lt;p&gt;So lets learn how to prune unused and unnecessary &lt;strong&gt;images, containers, volumes and networks&lt;/strong&gt;!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This tutorial will help you liberate space on your system without breaking anything in the process.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Before we start
&lt;/h2&gt;

&lt;p&gt;We will be using the Docker CLI, so I expect you to be a bit familiar with it.&lt;/p&gt;

&lt;p&gt;Otherwise just use &lt;code&gt;docker --help&lt;/code&gt; on the terminal and toy with it a little.&lt;/p&gt;

&lt;h3&gt;
  
  
  Requisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;A little bit of Docker knowledge&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Images
&lt;/h2&gt;

&lt;p&gt;Remove all images that are not tagged or referenced by any container&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker image prune
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Containers
&lt;/h2&gt;

&lt;p&gt;Remove all stopped containers&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker container prune
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Volumes
&lt;/h2&gt;

&lt;p&gt;Remove all volumes not used by at least one container&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker volume prune
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Networks
&lt;/h2&gt;

&lt;p&gt;Remove all networks not used by at least one container&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker network prune
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Everything
&lt;/h2&gt;

&lt;p&gt;To finalize, lets remove everything --&lt;em&gt;but volumes&lt;/em&gt;-- with a single command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker system prune
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to remove volumes too, just append &lt;code&gt;--volumes&lt;/code&gt; at the end.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker system prune &lt;span class="nt"&gt;--volumes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;And voila, that removed every single resource that was unnecessary on your system!&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;p&gt;You might find that some images can't be removed because they are used. In this case you want to remove the resource that is using it, most likely a container.&lt;/p&gt;




&lt;h2&gt;
  
  
  End
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What's next?
&lt;/h3&gt;

&lt;p&gt;If you want to read more, please check out the &lt;a href="https://docs.docker.com/config/pruning/"&gt;official Docker guide to pruning&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Self promotion
&lt;/h3&gt;

&lt;p&gt;If you have found this useful then you should follow me, I will be posting more interesting content! 🥰&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/github"&gt;GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/twitter"&gt;Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/dev.to"&gt;Dev.to&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/medium"&gt;Medium&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/linkedin"&gt;LinkedIn&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Or support me financially. 💸&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/github/sponsor"&gt;GitHub Sponsors&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/liberapay"&gt;LiberaPay&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/paypal"&gt;PayPal&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Today you have learned how to free up space on your system by removing Docker's unused images, containers, volumes and networks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Let me know how much space you have recovered in the comments!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>devops</category>
      <category>cloud</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to free up disk space on Ubuntu Server</title>
      <dc:creator>Alejandro Akbal</dc:creator>
      <pubDate>Tue, 15 Dec 2020 11:04:47 +0000</pubDate>
      <link>https://dev.to/alejandroakbal/how-to-free-up-disk-space-on-ubuntu-server-222h</link>
      <guid>https://dev.to/alejandroakbal/how-to-free-up-disk-space-on-ubuntu-server-222h</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;So you have been running your Ubuntu Server for a while and &lt;strong&gt;recently found out that the disk usage is already at 70%!?&lt;/strong&gt; Then lets free some space up.&lt;/p&gt;

&lt;p&gt;This tutorial will help you liberate space on your system without breaking anything in the process.&lt;/p&gt;




&lt;h2&gt;
  
  
  Before we start
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Preface
&lt;/h3&gt;

&lt;p&gt;While this tutorial is focused on &lt;strong&gt;Ubuntu Server&lt;/strong&gt;, it can be used for many other distributions that use the same packages, like Ubuntu Desktop, Debian, Linux Mint, etc.&lt;/p&gt;

&lt;h3&gt;
  
  
  Requisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;An Ubuntu server&lt;/li&gt;
&lt;li&gt;Access to your server&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Clean packages
&lt;/h2&gt;

&lt;p&gt;Packages are archived and stored, if these versions can't be downloaded anymore --&lt;em&gt;because there is a newer version or any other reason&lt;/em&gt;--, they end up being unnecessary. So lets clean lingering packages.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Find no longer available packages and remove them&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt autoclean &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Remove packages
&lt;/h2&gt;

&lt;p&gt;Chances are that &lt;strong&gt;when you update and upgrade your system, some packages end up being unnecessary&lt;/strong&gt;. But your system won't remove them, so lets tell it to do that.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Find unnecessary or redundant packages and remove them&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt autoremove &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Logs
&lt;/h2&gt;

&lt;p&gt;Application logs keep increasing the disk usage of your server, &lt;strong&gt;specially if it is a busy one&lt;/strong&gt;. But if we don't care much about keeping records, we can just delete them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check current logs disk usage&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;journalctl &lt;span class="nt"&gt;--disk-usage&lt;/span&gt;

&lt;span class="c"&gt;# Rotate logs so they are saved to disk&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;journalctl &lt;span class="nt"&gt;--rotate&lt;/span&gt;

&lt;span class="c"&gt;# Clean any log that is older than one second&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;journalctl &lt;span class="nt"&gt;--vacuum-time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1s

&lt;span class="c"&gt;# One liner&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;journalctl &lt;span class="nt"&gt;--rotate&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;journalctl &lt;span class="nt"&gt;--vacuum-time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Biggest files
&lt;/h2&gt;

&lt;p&gt;Now we are switching to a more manual approach, lets find out what the biggest files on our system are.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Find biggest files in "/" and show their size in human readable format
sudo du -a -h /

# Sort the output
sort -n -r

# Show only the top 15 results
head -n 15

# Combined in a one liner
sudo du -a -h / | sort -n -r | head -n 15
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then delete them&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Delete a file&lt;/span&gt;
&lt;span class="nb"&gt;sudo rm&lt;/span&gt; /path/to/file
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;⚠ Be careful to not delete any important file, in case of doubt, don't do it. ⚠&lt;/p&gt;




&lt;h2&gt;
  
  
  End
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What next?
&lt;/h3&gt;

&lt;p&gt;You can now search for more specific guides.&lt;/p&gt;

&lt;p&gt;For example, if you are using Docker, you might want to learn &lt;a href="https://blog.akbal.dev/how-to-remove-unused-docker-resources"&gt;how to remove unnecessary resources&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Self promotion
&lt;/h3&gt;

&lt;p&gt;If you have found this useful then you should follow me, I will be posting more interesting content! 🥰&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/github"&gt;GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/twitter"&gt;Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/dev.to"&gt;Dev.to&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/medium"&gt;Medium&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/linkedin"&gt;LinkedIn&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Or support me financially. 💸&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/github/sponsor"&gt;GitHub Sponsors&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/liberapay"&gt;LiberaPay&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/paypal"&gt;PayPal&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Today you have learned how to free up space on your system by removing packages, logs and files.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Let me know how much space you have recovered in the comments!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ubuntu</category>
      <category>security</category>
      <category>devops</category>
      <category>server</category>
    </item>
    <item>
      <title>How to completely secure an Ubuntu server</title>
      <dc:creator>Alejandro Akbal</dc:creator>
      <pubDate>Wed, 02 Dec 2020 12:03:30 +0000</pubDate>
      <link>https://dev.to/alejandroakbal/how-to-completely-secure-an-ubuntu-server-55i2</link>
      <guid>https://dev.to/alejandroakbal/how-to-completely-secure-an-ubuntu-server-55i2</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;This tutorial will help you to set up a secure Ubuntu server from scratch.&lt;/p&gt;

&lt;p&gt;Keep in mind that this is not a common tutorial, &lt;strong&gt;this is the culmination of all the knowledge I have gathered from managing my own servers&lt;/strong&gt; for more than three years.&lt;/p&gt;




&lt;h2&gt;
  
  
  Before we start
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Preface
&lt;/h3&gt;

&lt;p&gt;While this tutorial is focused on &lt;strong&gt;Ubuntu 20.04&lt;/strong&gt;, it can be used for many other versions, like 18.04 and 16.04. As they are very similar.&lt;/p&gt;

&lt;h3&gt;
  
  
  Requisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;An Ubuntu server&lt;/li&gt;
&lt;li&gt;Access to your server&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;It doesn't matter if your server is hosted on DigitalOcean, Google Cloud Engine or Amazon Web Services, Ubuntu should be the same.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Requisite info
&lt;/h3&gt;

&lt;p&gt;If you don't have a server you might want to look at the Useful resources step.&lt;/p&gt;




&lt;h2&gt;
  
  
  Updates
&lt;/h2&gt;

&lt;p&gt;The first and probably most important step is to &lt;strong&gt;always keep the system up-to-date&lt;/strong&gt;. To do so just open the terminal to update and upgrade the packages via &lt;a href="https://linuxize.com/post/how-to-use-apt-command/"&gt;apt&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update           &lt;span class="c"&gt;# Update package information&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt full-upgrade &lt;span class="nt"&gt;-y&lt;/span&gt;  &lt;span class="c"&gt;# Upgrade packages&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt autoremove &lt;span class="nt"&gt;-y&lt;/span&gt;    &lt;span class="c"&gt;# Remove unnecessary packages&lt;/span&gt;

&lt;span class="c"&gt;# One liner&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt dist-upgrade &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt autoremove &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Automatic updates
&lt;/h2&gt;

&lt;p&gt;Now that the packages are updated, we should install an automated solution to keep the system always up-to-date.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://linuxize.com/post/how-to-set-up-automatic-updates-on-ubuntu-18-04/"&gt;This tutorial on Linuxize&lt;/a&gt; will help you install and configure the &lt;code&gt;unattended-upgrades&lt;/code&gt; package, which is exactly what is needed.&lt;/p&gt;




&lt;h2&gt;
  
  
  New user
&lt;/h2&gt;

&lt;p&gt;Using the default super user &lt;code&gt;root&lt;/code&gt; is always &lt;strong&gt;bad practice&lt;/strong&gt;, it does everything with the maximum level of permissions, allowing you to break anything; and more critically... &lt;em&gt;Access to anything on the system&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Instead, we should use a normal user with super user &lt;strong&gt;privileges&lt;/strong&gt;. &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-create-a-new-sudo-enabled-user-on-ubuntu-20-04-quickstart"&gt;This tutorial on DigitalOcean&lt;/a&gt; will guide you to do that.&lt;/p&gt;




&lt;h2&gt;
  
  
  SSH credentials
&lt;/h2&gt;

&lt;p&gt;Now that you have a new user with super user privileges, you might want to SSH in your server with it, &lt;em&gt;but might find that you can't&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;This is because the credentials were stored on the user you were using before, most likely &lt;code&gt;root&lt;/code&gt;. Just SSH again with the previous user and copy the credentials to the new user with the &lt;code&gt;rsync&lt;/code&gt; utility package.&lt;/p&gt;

&lt;p&gt;Follow the &lt;strong&gt;5th step&lt;/strong&gt; of &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-20-04#if-the-root-account-uses-ssh-key-authentication"&gt;this tutorial on DigitalOcean&lt;/a&gt; to do so.&lt;/p&gt;




&lt;h2&gt;
  
  
  SSHD
&lt;/h2&gt;

&lt;p&gt;SSHD manages the SSH connections to the server. Its default configuration is good but some changes must be made, like disabling the &lt;code&gt;root&lt;/code&gt; user login and changing the default &lt;code&gt;SSH&lt;/code&gt; port.&lt;/p&gt;

&lt;p&gt;Follow the &lt;strong&gt;first step&lt;/strong&gt; &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-harden-openssh-on-ubuntu-18-04"&gt;of this tutorial on DigitalOcean&lt;/a&gt; to learn how to configure SSHD.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It is recommended that you change the default &lt;code&gt;SSH&lt;/code&gt; port&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  UFW
&lt;/h2&gt;

&lt;p&gt;UFW is Ubuntu's default firewall and is extremely useful. By default it allows &lt;code&gt;http&lt;/code&gt; and &lt;code&gt;ssh&lt;/code&gt; connections, depending of your use case you might not need some of those rules.&lt;/p&gt;

&lt;p&gt;Check out &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-set-up-a-firewall-with-ufw-on-ubuntu-20-04"&gt;this tutorial on DigitalOcean&lt;/a&gt; to learn how to configure UFW.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you changed the &lt;code&gt;SSH&lt;/code&gt; port on an earlier step, you might want to create a new UFW rule for that port.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Fail2Ban
&lt;/h2&gt;

&lt;p&gt;Fail2Ban protects you from brute-force attacks. It bans bad actors from accessing the server for a specified quantity of time.&lt;/p&gt;

&lt;p&gt;Learn how to install and configure Fail2Ban &lt;a href="https://linuxize.com/post/install-configure-fail2ban-on-ubuntu-20-04/"&gt;with this tutorial on Linuxize&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Miscellaneous
&lt;/h2&gt;

&lt;p&gt;These are some &lt;strong&gt;quick specific guides&lt;/strong&gt; that you should keep in mind if you use any of this software.&lt;/p&gt;

&lt;h3&gt;
  
  
  Nginx
&lt;/h3&gt;

&lt;p&gt;Nginx has various configuration files, its defaults are pretty good but you might want to take a look at it.&lt;/p&gt;

&lt;p&gt;Use &lt;a href="https://www.acunetix.com/blog/web-security-zone/hardening-nginx/"&gt;this tutorial on Acunetix&lt;/a&gt; as a starting point.&lt;/p&gt;

&lt;p&gt;There is also &lt;a href="https://www.digitalocean.com/community/tools/nginx"&gt;this pretty nifty tool by DigitalOcean&lt;/a&gt; that allows you to configure Nginx in a visual manner. It includes popular presets, for example for NodeJS and PHP applications.&lt;/p&gt;

&lt;h3&gt;
  
  
  Apache2
&lt;/h3&gt;

&lt;p&gt;Apache might require more work, as its defaults leak some information about your system.&lt;/p&gt;

&lt;p&gt;Start your configuration journey with &lt;a href="https://www.tecmint.com/apache-security-tips/"&gt;this tutorial by Tecmint&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  PHP
&lt;/h3&gt;

&lt;h3&gt;
  
  
  Database
&lt;/h3&gt;

&lt;p&gt;I have used MySQL and MariaDB on the past, by default their ports are opened externally, that shouldn't be allowed, as it is a security risk.&lt;/p&gt;

&lt;p&gt;The database should only be allowed from local connections; or if ran externally, by whitelisted IPs.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Learn how to configure MySQL with &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-mysql-on-ubuntu-20-04"&gt;this tutorial on DigitalOcean&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Learn how to configure MariaDB with &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-mariadb-on-ubuntu-20-04"&gt;this tutorial on DigitalOcean&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Recommendations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Dokku
&lt;/h3&gt;

&lt;p&gt;Now that you have your own secure infrastructure, you might want to create applications and services.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://dokku.viewdocs.io/dokku/"&gt;Dokku&lt;/a&gt; is perfect for that. It allows you to containerize, build and run your applications with a simple &lt;code&gt;git push&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Check out &lt;a href="https://dev.to/alejandroakbal/create-your-own-heroku-with-dokku-on-digitalocean-14ef"&gt;my own tutorial&lt;/a&gt; to learn how to set up and use Dokku.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;There are some parts that you might want to skip, as they are similar to this tutorial.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  End
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Useful resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.to/phocks/how-to-get-a-free-google-server-forever-1fpf"&gt;How to get a free Google server forever&lt;/a&gt;, a perfect test environment for this tutorial.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/phocks/how-to-get-2x-oracle-cloud-servers-free-forever-4o22"&gt;How to get 2x Oracle Cloud servers free forever&lt;/a&gt;, a more powerful alternative to the free GCE server.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/alejandroakbal/create-your-own-heroku-with-dokku-on-digitalocean-14ef"&gt;Create your own Heroku with Dokku on DigitalOcean&lt;/a&gt;, a guide to deploy your applications to your now-secure server.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Self promotion
&lt;/h3&gt;

&lt;p&gt;If you have found this tutorial useful then you should follow me, I will be posting more interesting content! :')&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/github"&gt;GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/twitter"&gt;Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/dev.to"&gt;Dev.to&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/medium"&gt;Medium&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/linkedin"&gt;LinkedIn&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Or support me financially. &amp;lt;3&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/github/sponsor"&gt;GitHub Sponsors&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/liberapay"&gt;LiberaPay&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redirect.akbal.dev/paypal"&gt;PayPal&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Credit
&lt;/h3&gt;

&lt;p&gt;Thanks to&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Any linked website and community for their wonderful tutorials and help&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ubuntu</category>
      <category>security</category>
      <category>devops</category>
      <category>server</category>
    </item>
  </channel>
</rss>
