<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Vivek's Blog]]></title><description><![CDATA[Vivek's Blog]]></description><link>https://blog.vivekkaushik.com</link><generator>RSS for Node</generator><lastBuildDate>Tue, 21 Apr 2026 20:06:44 GMT</lastBuildDate><atom:link href="https://blog.vivekkaushik.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Zero‑Downtime PostgreSQL Migration Using Logical Replication]]></title><description><![CDATA[Migrating a production database is often a high-stakes operation. Traditional "dump and restore" methods require significant maintenance windows, leading to service downtime. To solve this, PostgreSQL offers Logical Replication, a native mechanism th...]]></description><link>https://blog.vivekkaushik.com/zerodowntime-postgresql-migration-using-logical-replication</link><guid isPermaLink="true">https://blog.vivekkaushik.com/zerodowntime-postgresql-migration-using-logical-replication</guid><category><![CDATA[PostgreSQL]]></category><category><![CDATA[Databases]]></category><category><![CDATA[database]]></category><category><![CDATA[migrations]]></category><category><![CDATA[logical replication]]></category><dc:creator><![CDATA[Vivek Kaushik]]></dc:creator><pubDate>Wed, 17 Dec 2025 17:36:56 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1765992934436/2363fc77-f9e9-4c1d-9f34-7a2a1e027af9.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Migrating a production database is often a high-stakes operation. Traditional "dump and restore" methods require significant maintenance windows, leading to service downtime. To solve this, PostgreSQL offers <strong>Logical Replication</strong>, a native mechanism that allows data to stream from a source to a destination in real-time.</p>
<h2 id="heading-how-logical-replication-works">How Logical Replication Works</h2>
<p>At its core, logical replication leverages the <strong>Write-Ahead Log (WAL)</strong>. While physical replication copies disk blocks, logical replication decodes the WAL into row-level changes (INSERTs, UPDATEs, and DELETEs).</p>
<ol>
<li><p><strong>The Publisher:</strong> On the source database, you define a <strong>Publication</strong>. This serves as a set of change sets for specific tables.</p>
</li>
<li><p><strong>The Subscriber:</strong> On the destination database, you create a <strong>Subscription</strong>. The subscriber connects to the publisher, pulls an initial snapshot of the data, and then continuously "replays" ongoing changes.</p>
</li>
<li><p><strong>The Replication Slot:</strong> The publisher uses a replication slot to ensure that WAL files are not deleted until the subscriber has confirmed receipt. This prevents data loss during transient network failures.</p>
</li>
</ol>
<h2 id="heading-step-1-configuring-the-source-publisher">Step 1: Configuring the Source (Publisher)</h2>
<p>Before starting, ensure your <code>wal_level</code> is set to <code>logical</code> in your <code>postgresql.conf</code>. For managed db this can be done by setting some flag from your cloud provider database management page.</p>
<p>To begin, connect to your source database and create a publication. You can choose to replicate the entire database or specific tables:</p>
<pre><code class="lang-pgsql"><span class="hljs-comment">-- create publication for all tables</span>
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">PUBLICATION</span> &lt;publication_name&gt; <span class="hljs-keyword">FOR</span> <span class="hljs-keyword">ALL</span> <span class="hljs-keyword">TABLE</span>;

<span class="hljs-comment">-- create publication only for some tables</span>
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">PUBLICATION</span> &lt;publication_name&gt; <span class="hljs-keyword">FOR</span> <span class="hljs-keyword">TABLE</span> tableA, tableB, tableC;

<span class="hljs-comment">-- Digital Ocean Managed Postgres Instance</span>
<span class="hljs-keyword">SELECT</span> aiven_extras.pg_create_publication_for_all_tables(<span class="hljs-string">'&lt;publication_name&gt;'</span>, <span class="hljs-string">'INSERT,UPDATE,DELETE,TRUNCATE'</span>)

<span class="hljs-comment">-- Verify the publication</span>
<span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> pg_publication;
</code></pre>
<h2 id="heading-step-2-preparing-the-destination-subscriber">Step 2: Preparing the Destination (Subscriber)</h2>
<p><strong>Important:</strong> Logical replication does not replicate schema definitions (DDL). You must ensure the table structures exist on the destination database before starting the subscription.</p>
<p>Once the schema is ready, connect to the destination and initiate the subscription:</p>
<pre><code class="lang-pgsql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">SUBSCRIPTION</span> &lt;subscription_name_here&gt; 
    <span class="hljs-keyword">CONNECTION</span> <span class="hljs-string">'dbname=&lt;database_name_here&gt; host=localhost port=5432 sslmode=disabled user=postgres password=&lt;password_here&gt;'</span> 
    <span class="hljs-keyword">PUBLICATION</span> &lt;publication_name&gt; <span class="hljs-keyword">WITH</span> (slot_name = <span class="hljs-string">'&lt;slot_name_here&gt;'</span>, create_slot = <span class="hljs-keyword">true</span>, copy_data = <span class="hljs-keyword">true</span>);
</code></pre>
<h3 id="heading-managed-service-nuances">Managed Service Nuances</h3>
<p>If you are using managed PostgreSQL services, the standard <code>CREATE SUBSCRIPTION</code> command may be restricted due to permissions:</p>
<ul>
<li><strong>DigitalOcean (Aiven):</strong> Use the <a target="_blank" href="https://github.com/aiven/aiven-extras"><code>aiven_extras</code></a> extension:</li>
</ul>
<pre><code class="lang-pgsql"><span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> aiven_extras.pg_create_subscription(
    <span class="hljs-string">'&lt;subscription_name_here&gt;'</span>, 
    <span class="hljs-string">'dbname=&lt;database_name_here&gt; host=localhost port=5432 sslmode=disabled user=postgres password=&lt;password_here&gt;'</span>, 
    <span class="hljs-string">'&lt;publication_name&gt;'</span>, 
    <span class="hljs-string">'&lt;slot_name_here&gt;'</span>, 
    <span class="hljs-keyword">TRUE</span>, <span class="hljs-keyword">TRUE</span>
);
</code></pre>
<ul>
<li><strong>Google Cloud SQL:</strong> Ensure the <code>cloudsql.logical_decoding</code> flag is set to <code>on</code>.</li>
</ul>
<h2 id="heading-step-3-monitoring-the-migration">Step 3: Monitoring the Migration</h2>
<p>The migration happens in two phases: the <strong>initial sync</strong> (copying existing data) and <strong>streaming</strong> (applying delta changes). You can monitor the progress with these queries:</p>
<pre><code class="lang-pgsql"><span class="hljs-keyword">SELECT</span> 
    n.nspname || <span class="hljs-string">'.'</span> || c.relname <span class="hljs-keyword">AS</span> <span class="hljs-built_in">table_name</span>,
    sr.srsubstate <span class="hljs-keyword">AS</span> state,
    <span class="hljs-keyword">CASE</span> sr.srsubstate
        <span class="hljs-keyword">WHEN</span> <span class="hljs-string">'i'</span> <span class="hljs-keyword">THEN</span> <span class="hljs-string">'initialize'</span>
        <span class="hljs-keyword">WHEN</span> <span class="hljs-string">'d'</span> <span class="hljs-keyword">THEN</span> <span class="hljs-string">'data copying'</span>
        <span class="hljs-keyword">WHEN</span> <span class="hljs-string">'s'</span> <span class="hljs-keyword">THEN</span> <span class="hljs-string">'synchronized'</span> 
        <span class="hljs-keyword">WHEN</span> <span class="hljs-string">'f'</span> <span class="hljs-keyword">THEN</span> <span class="hljs-string">'finishedcopy'</span>
        <span class="hljs-keyword">WHEN</span> <span class="hljs-string">'r'</span> <span class="hljs-keyword">THEN</span> <span class="hljs-string">'ready'</span>
    <span class="hljs-keyword">END</span> <span class="hljs-keyword">AS</span> state_description
<span class="hljs-keyword">FROM</span> pg_subscription_rel sr
<span class="hljs-keyword">JOIN</span> pg_class c <span class="hljs-keyword">ON</span> c.oid = sr.srrelid
<span class="hljs-keyword">JOIN</span> pg_namespace n <span class="hljs-keyword">ON</span> n.oid = c.relnamespace;

<span class="hljs-comment">-- to see the ongoing connections</span>
<span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> pg_stat_activity;
</code></pre>
<h3 id="heading-monitor-replication-lag">Monitor Replication Lag</h3>
<p>To ensure the destination is caught up before you perform the final cutover, check the lag:</p>
<pre><code class="lang-pgsql"><span class="hljs-comment">-- for checking current slot progress</span>
<span class="hljs-keyword">SELECT</span>
  slot_name,
  plugin,
  slot_type,
  active,
  confirmed_flush_lsn,
  pg_wal_lsn_diff(pg_current_wal_lsn(), confirmed_flush_lsn) <span class="hljs-keyword">AS</span> slot_lag_bytes
<span class="hljs-keyword">FROM</span>
  pg_replication_slots
<span class="hljs-keyword">WHERE</span>
  slot_type = <span class="hljs-string">'logical'</span>;
</code></pre>
<h2 id="heading-step-4-final-cutover-and-cleanup">Step 4: Final Cutover and Cleanup</h2>
<p>Once the lag is near zero, point your application services to the new destination database. After the migration is confirmed successful, clean up the replication resources to free up system overhead.</p>
<ol>
<li><p>Drop the Subscription:</p>
<pre><code class="lang-pgsql"> <span class="hljs-comment">-- Standard</span>
 <span class="hljs-keyword">DROP</span> <span class="hljs-keyword">SUBSCRIPTION</span> &lt;subscription_name&gt;;

 <span class="hljs-comment">-- for digital ocean managed postgres instance</span>
 <span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> aiven_extras.pg_drop_subscription(<span class="hljs-string">'&lt;subscription_name&gt;'</span>);

 <span class="hljs-comment">-- If you somehow dropped the publication before removing subscription drop command will get stuck trying to remove slot on publisher</span>
 <span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">SUBSCRIPTION</span> mysub <span class="hljs-keyword">DISABLE</span>;
 <span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">SUBSCRIPTION</span> mysub <span class="hljs-keyword">SET</span> (slot_name = <span class="hljs-keyword">NONE</span>);
</code></pre>
</li>
<li><p>Drop the Publication (on Source):</p>
<pre><code class="lang-pgsql"> <span class="hljs-comment">-- Finally drop the publication</span>
 <span class="hljs-keyword">DROP</span> <span class="hljs-keyword">publication</span> &lt;publication_name&gt;;
</code></pre>
</li>
<li><p>Remove the Slot (if it persists):</p>
<pre><code class="lang-pgsql"> <span class="hljs-comment">-- slots should be automatically deleted but in case you want to manually delete one</span>
 <span class="hljs-keyword">SELECT</span> pg_drop_replication_slot(<span class="hljs-string">'&lt;slot_name&gt;'</span>);
</code></pre>
</li>
</ol>
<p><strong>Sequences are Not Replicated!</strong></p>
<p>Logical replication only copies data changes. It does <strong>not</strong> keep sequences (used for auto-incrementing IDs) in sync. You can use the following query to get the current sequence values</p>
<pre><code class="lang-pgsql"><span class="hljs-comment">-- Get all the sequences and their respective value</span>
<span class="hljs-keyword">SELECT</span> t.sequence_name, nextval(t.sequence_name::<span class="hljs-type">text</span>) - <span class="hljs-number">1</span> <span class="hljs-keyword">FROM</span> (<span class="hljs-keyword">SELECT</span> sequence_schema, sequence_name
<span class="hljs-keyword">FROM</span> information_schema.<span class="hljs-keyword">sequences</span>) <span class="hljs-keyword">AS</span> t;

<span class="hljs-comment">-- Set the value</span>
<span class="hljs-keyword">SELECT</span> setval(<span class="hljs-string">'&lt;sequence_name&gt;'</span>, &lt;value_here&gt;);
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Logical replication is an elegant solution for moving data with minimal impact on users. By following these steps—Publication, Subscription, and Monitoring—you can migrate gigabytes or terabytes of data while keeping your application online.</p>
]]></content:encoded></item><item><title><![CDATA[Keeping Dokku Apps Fresh: The Easy Way to Update Deployed Images]]></title><description><![CDATA[Ever found yourself in the endless cycle of "delete Docker image, redeploy, repeat" to keep your Dokku apps updated? You’re not alone. If your Dokku-deployed application relies directly on a Docker image (perhaps from Docker Hub or your private regis...]]></description><link>https://blog.vivekkaushik.com/keeping-dokku-apps-fresh-the-easy-way-to-update-deployed-images</link><guid isPermaLink="true">https://blog.vivekkaushik.com/keeping-dokku-apps-fresh-the-easy-way-to-update-deployed-images</guid><category><![CDATA[Dokku]]></category><dc:creator><![CDATA[Vivek Kaushik]]></dc:creator><pubDate>Wed, 30 Jul 2025 20:24:42 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1753906975717/9f1fd862-f410-4089-805e-dff5add6085f.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Ever found yourself in the endless cycle of "delete Docker image, redeploy, repeat" to keep your Dokku apps updated? You’re not alone. If your Dokku-deployed application relies directly on a Docker image (perhaps from Docker Hub or your private registry), you might've noticed a frustrating behavior: once Dokku pulls the image even when you use the <code>latest</code> tag it won’t fetch updates unless you manually delete the old image.<br />This got me scratching my head until I stumbled onto an elegant fix that streamlines the workflow and fits perfectly into the minimalist, automation-loving philosophy I share here.</p>
<h2 id="heading-the-problem">The Problem</h2>
<p>By default, Dokku holds onto the Docker image it fetched during the initial deploy. No matter how many times you rebuild or redeploy with the same tag, Dokku won’t pull a fresher image from the registry unless you interfere by deleting it manually.</p>
<h2 id="heading-the-solution-automate-image-updates-with-build-options">The Solution: Automate Image Updates with Build Options</h2>
<p>Dokku’s flexibility shines if you know where to look. The trick is to tell Dokku to pull the latest image from your registry <strong>every time you rebuild</strong>—completely bypassing the need to manually delete old images.</p>
<h2 id="heading-step-1-add-docker-build-options-to-your-app">Step 1: Add Docker Build Options to Your App</h2>
<p>You'll use Dokku's <code>docker-options</code> to modify how the build and deploy steps work for your app.</p>
<pre><code class="lang-bash">dokku docker-options:add &lt;app-name&gt; build --pull --no-cache
</code></pre>
<ul>
<li><p>Replace <code>&lt;app-name&gt;</code> with the name of your Dokku app.</p>
</li>
<li><p><code>--pull</code> makes sure Docker pulls the latest version of the image from your registry.</p>
</li>
<li><p><code>--no-cache</code> ensures Docker doesn’t use a cached image locally.</p>
</li>
</ul>
<h2 id="heading-step-2-rebuild-your-app">Step 2: Rebuild Your App</h2>
<p>Now, whenever you want to update to the latest image (say, after pushing to your Docker registry), run:</p>
<pre><code class="lang-bash">dokku ps:rebuild &lt;app-name&gt;
</code></pre>
<p>That's it! Dokku will pull down the freshest image, rebuild, and restart your app—no more manual deletions required.</p>
<h2 id="heading-thats-a-wrap">That’s a Wrap</h2>
<p>This approach works wonders for keeping your Dokku deployments lean and up-to-date.</p>
]]></content:encoded></item><item><title><![CDATA[Internet Failover with Netplan and iptables on Ubuntu 24.04]]></title><description><![CDATA[Lately, my internet has been throwing tantrums — not because the connection itself is bad, but because every time the power blinks, my router decides it's time for a little nap. Naturally, the obvious solution was to slap a power backup on the router...]]></description><link>https://blog.vivekkaushik.com/internet-failover-with-netplan-and-iptables-on-ubuntu-2404</link><guid isPermaLink="true">https://blog.vivekkaushik.com/internet-failover-with-netplan-and-iptables-on-ubuntu-2404</guid><category><![CDATA[internet-failover]]></category><category><![CDATA[Ubuntu]]></category><category><![CDATA[router]]></category><category><![CDATA[internet]]></category><category><![CDATA[networking]]></category><category><![CDATA[netplan]]></category><dc:creator><![CDATA[Vivek Kaushik]]></dc:creator><pubDate>Fri, 13 Jun 2025 08:40:52 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1749801363161/4c1c2ca1-5042-43ec-bf2c-1455c2f747b7.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Lately, my internet has been throwing tantrums — not because the connection itself is bad, but because every time the power blinks, my router decides it's time for a little nap. Naturally, the obvious solution was to slap a power backup on the router (which I did — not becuase my plan didn’t work but because my secondary router is a 4G router and has the speed of a potato on dial-up).</p>
<p>But all this got me thinking: what if I could set up a proper network failover? You know, like having a backup parachute in case the first one gets stage fright. That would be pretty cool — automatic protection against actual internet outages, and I could keep streaming, working, or doom-scrolling without missing a beat.</p>
<p>Here is what I ended up with</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1749801534798/888c4be3-8acd-479f-9ba5-7d1baef54db0.png" alt class="image--center mx-auto" /></p>
<p>There was a lot of hacking involved to get to this point, let’s dive in.</p>
<h3 id="heading-the-first-problem-may-not-be-a-problem-for-you">The First Problem (May Not Be a Problem for You)</h3>
<p>So the first issue I ran into — which you <em>might</em> not face — was that my portable Jio router decided to play hide-and-seek by changing its interface name and MAC address every time it rebooted. Super helpful, right? This made it impossible to apply any persistent configuration.</p>
<p>If your setup doesn’t suffer from this little identity crisis, you can probably skip this part. But if you do — welcome to the club — here’s how I fixed it using <strong>udev rules</strong>.</p>
<p><strong>Step 1: Identify your device</strong></p>
<p>First, you’ll need to find your device info:</p>
<pre><code class="lang-bash">udevadm info -a -p /sys/class/net/enxXXXXXXX
</code></pre>
<p>(Replace <code>enxXXXXXXX</code> with your actual interface name.)<br />Look through the output and note down the important bits, like:</p>
<pre><code class="lang-bash">ATTRS{idVendor}==<span class="hljs-string">"0bda"</span>
ATTRS{idProduct}==<span class="hljs-string">"8153"</span>
ATTRS{serial}==<span class="hljs-string">"000001"</span>
</code></pre>
<p><strong>Step 2: Create a udev rule</strong></p>
<p>Now that we’ve got what we need, let’s make the rule:</p>
<pre><code class="lang-bash">sudo nano /etc/udev/rules.d/70-persistent-net.rules
</code></pre>
<p>Add this line:</p>
<pre><code class="lang-bash">SUBSYSTEM==<span class="hljs-string">"net"</span>, ACTION==<span class="hljs-string">"add"</span>, ATTRS{idVendor}==<span class="hljs-string">"0bda"</span>, ATTRS{idProduct}==<span class="hljs-string">"8153"</span>, ATTRS{serial}==<span class="hljs-string">"000001"</span>, NAME=<span class="hljs-string">"usbnet0"</span>, RUN+=<span class="hljs-string">"/sbin/ip link set usbnet0 address 02:11:22:33:44:55"</span>
</code></pre>
<ul>
<li><p>Replace <code>02:11:22:33:44:55</code> with whatever MAC address you want.</p>
</li>
<li><p><code>usbnet0</code> is a custom interface name that will now stay consistent (finally).usbnet0 is a custom interface name that will now always be used.</p>
</li>
</ul>
<p><strong>Step 3: Reload rules or replug device</strong><br />You can reload the rules like this:</p>
<pre><code class="lang-bash">sudo udevadm control --reload
sudo udevadm trigger
</code></pre>
<p>Or just take the lazy way like I did and unplug/replug the USB device. Done.</p>
<h3 id="heading-the-second-part-enter-the-failover-magic">The Second Part: Enter the Failover Magic</h3>
<p>Now that we've successfully tamed our devices and given them proper names (so they finally know who they are), it’s time for the fun part — <strong>creating the actual failover system.</strong></p>
<p>At this stage, both internet connections are plugged in and ready, but here’s the catch: Linux will only use one of them for traffic at any given time. This decision is made by something called the <strong>routing table</strong>. Think of it like Google Maps for your packets — it tells your data which road (interface) to take.</p>
<p>Now, in Linux, there’s a thing called the <strong>default route</strong> — basically the "main road" your traffic takes if there are no better instructions. You can have multiple default routes, and each can be assigned a weight (or "metric"). The lower the weight, the more Linux favors it. And <em>that</em> little piece of knowledge is what makes this whole thing work.</p>
<h3 id="heading-step-1-install-keepalived">Step 1: Install <code>keepalived</code></h3>
<p>We’re going to use <strong>keepalived</strong> to automate the failover. It’ll run our script at regular intervals and switch routes if it detects trouble on the primary connection.</p>
<p>If you don’t have <code>keepalived</code> installed yet:</p>
<pre><code class="lang-bash">sudo apt install keepalived
</code></pre>
<h3 id="heading-step-2-create-the-keepalived-configuration">Step 2: Create the keepalived configuration</h3>
<p>Now create (or edit) the file at <code>/etc/keepalived/keepalived.conf</code>:</p>
<pre><code class="lang-bash">sudo nano /etc/keepalived/keepalived.conf
</code></pre>
<p>Paste this in:</p>
<pre><code class="lang-bash">vrrp_script chk_downtime {
    script <span class="hljs-string">"/etc/keepalived/check_downtime.sh"</span>
    interval 3
    weight +20
}

vrrp_instance FAILOVER {
    state MASTER
    interface wlp2s0
    virtual_router_id 51
    priority 200
    advert_int 2

    track_script {
        chk_downtime
    }

    notify_master <span class="hljs-string">"/etc/keepalived/failover.sh"</span>
    notify_backup <span class="hljs-string">"/etc/keepalived/failover.sh"</span>
}
</code></pre>
<h3 id="heading-step-3-write-the-downtime-check-script">Step 3: Write the downtime check script</h3>
<p>This little script checks if your primary connection is still alive.</p>
<p>Create the file:</p>
<pre><code class="lang-bash">sudo nano /etc/keepalived/check_downtime.sh
</code></pre>
<p>Paste:</p>
<pre><code class="lang-bash"><span class="hljs-meta">#!/bin/bash</span>

logger <span class="hljs-string">"check_wlp3s0: Checking internet Access via Wifi"</span>
TARGET=<span class="hljs-string">"8.8.4.4"</span>
INTERFACE=<span class="hljs-string">"wlp2s0"</span>
GATEWAY=<span class="hljs-string">"192.168.1.1"</span>

<span class="hljs-comment"># Check current route</span>
CURRENT_ROUTE=$(ip route get <span class="hljs-variable">$TARGET</span> 2&gt;/dev/null)

<span class="hljs-keyword">if</span> ! <span class="hljs-built_in">echo</span> <span class="hljs-string">"<span class="hljs-variable">$CURRENT_ROUTE</span>"</span> | grep -q <span class="hljs-string">"dev <span class="hljs-variable">$INTERFACE</span>"</span>; <span class="hljs-keyword">then</span>
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"Route to <span class="hljs-variable">$TARGET</span> is not via <span class="hljs-variable">$INTERFACE</span>. Adding route..."</span>
    ip route add <span class="hljs-variable">$TARGET</span> via <span class="hljs-variable">$GATEWAY</span> dev <span class="hljs-variable">$INTERFACE</span>
<span class="hljs-keyword">fi</span>

ping -I wlp3s0 -c 2 -W 1 <span class="hljs-variable">$TARGET</span> &gt; /dev/null 2&gt;&amp;1

<span class="hljs-keyword">if</span> [ $? -eq 0 ]; <span class="hljs-keyword">then</span>
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"Internet is reachable via wlp3s0"</span>
    /etc/keepalived/set_interface.sh
    <span class="hljs-built_in">exit</span> 0
<span class="hljs-keyword">else</span>
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"Internet is not rechable via wlp3s0"</span>
    /etc/keepalived/set_interface.sh
    <span class="hljs-built_in">exit</span> 1
<span class="hljs-keyword">fi</span>
</code></pre>
<p>Don’t forget to give it execute permissions:</p>
<pre><code class="lang-bash">sudo chmod u+x /etc/keepalived/check_downtime.sh
</code></pre>
<h3 id="heading-step-4-create-the-failover-script">Step 4: Create the failover script</h3>
<p>Almost there — now let’s add the script that actually makes the switch.</p>
<p>Create:</p>
<pre><code class="lang-bash">sudo nano /etc/keepalived/failover.sh
</code></pre>
<p>Paste:</p>
<pre><code class="lang-bash"><span class="hljs-meta">#!/usr/bin/env bash</span>

<span class="hljs-comment"># Network Interface Switching Script</span>
<span class="hljs-comment"># Automatically switches between primary and secondary network interfaces</span>
<span class="hljs-comment"># based on connectivity tests and manages NAT rules accordingly</span>

<span class="hljs-comment"># set -o errexit   # Exit on any command failure [3]</span>
<span class="hljs-comment"># set -o pipefail  # Exit on pipe failure [3]</span>
<span class="hljs-comment"># set -o nounset   # Exit on undefined variables [3]</span>

<span class="hljs-comment"># ============================================================================</span>
<span class="hljs-comment"># CONFIGURATION</span>
<span class="hljs-comment"># ============================================================================</span>

<span class="hljs-built_in">readonly</span> TARGET_HOST=<span class="hljs-string">"8.8.4.4"</span>
<span class="hljs-built_in">readonly</span> PRIMARY_INTERFACE=<span class="hljs-string">"wlp2s0"</span>
<span class="hljs-built_in">readonly</span> SECONDARY_INTERFACE=<span class="hljs-string">"usbnet0"</span>
<span class="hljs-built_in">readonly</span> PRIMARY_GATEWAY=<span class="hljs-string">"192.168.1.1"</span>
<span class="hljs-built_in">readonly</span> SECONDARY_GATEWAY_DEFAULT=<span class="hljs-string">"192.168.225.1"</span>
<span class="hljs-built_in">readonly</span> NAT_SUBNET=<span class="hljs-string">"10.10.2.0/24"</span>
<span class="hljs-built_in">readonly</span> PRIMARY_METRIC=200
<span class="hljs-built_in">readonly</span> SECONDARY_METRIC=210

<span class="hljs-comment"># ============================================================================</span>
<span class="hljs-comment"># UTILITY FUNCTIONS</span>
<span class="hljs-comment"># ============================================================================</span>

<span class="hljs-comment"># Print error messages to stderr [10]</span>
<span class="hljs-function"><span class="hljs-title">error</span></span>() {
    <span class="hljs-built_in">printf</span> <span class="hljs-string">"ERROR: %s\n"</span> <span class="hljs-string">"$*"</span> &gt;&amp;2
}

<span class="hljs-comment"># Print informational messages</span>
<span class="hljs-function"><span class="hljs-title">info</span></span>() {
    <span class="hljs-built_in">printf</span> <span class="hljs-string">"INFO: %s\n"</span> <span class="hljs-string">"$*"</span>
}

<span class="hljs-comment"># Check if command succeeded [13]</span>
<span class="hljs-function"><span class="hljs-title">check_command</span></span>() {
    <span class="hljs-keyword">if</span> ! <span class="hljs-string">"<span class="hljs-variable">$@</span>"</span>; <span class="hljs-keyword">then</span>
        error <span class="hljs-string">"Command failed: $*"</span>
        <span class="hljs-built_in">return</span> 1
    <span class="hljs-keyword">fi</span>
}

<span class="hljs-comment"># ============================================================================</span>
<span class="hljs-comment"># NETWORK INTERFACE FUNCTIONS</span>
<span class="hljs-comment"># ============================================================================</span>

<span class="hljs-comment"># Check if network interface exists and is available</span>
<span class="hljs-function"><span class="hljs-title">is_interface_available</span></span>() {
    <span class="hljs-built_in">local</span> interface=<span class="hljs-string">"<span class="hljs-variable">$1</span>"</span>
    ip link show <span class="hljs-string">"<span class="hljs-variable">$interface</span>"</span> &amp;&gt;/dev/null
}

<span class="hljs-comment"># Get gateway for specific interface from routing table</span>
<span class="hljs-function"><span class="hljs-title">get_interface_gateway</span></span>() {
    <span class="hljs-built_in">local</span> interface=<span class="hljs-string">"<span class="hljs-variable">$1</span>"</span>
    ip route show default | grep <span class="hljs-string">"dev <span class="hljs-variable">$interface</span>"</span> | awk <span class="hljs-string">'{print $3}'</span> | head -1
}

<span class="hljs-comment"># Obtain gateway via DHCP for interface</span>
<span class="hljs-function"><span class="hljs-title">obtain_dhcp_gateway</span></span>() {
    <span class="hljs-built_in">local</span> interface=<span class="hljs-string">"<span class="hljs-variable">$1</span>"</span>
    <span class="hljs-built_in">local</span> gateway

    info <span class="hljs-string">"Attempting to obtain gateway via DHCP for <span class="hljs-variable">$interface</span>..."</span>

    <span class="hljs-comment"># Run dhclient and extract gateway from output [8]</span>
    gateway=$(dhclient -v <span class="hljs-string">"<span class="hljs-variable">$interface</span>"</span> 2&gt;&amp;1 | awk <span class="hljs-string">'/DHCPACK/ {print $NF}'</span> | head -1)

    <span class="hljs-keyword">if</span> [[ -n <span class="hljs-string">"<span class="hljs-variable">$gateway</span>"</span> ]]; <span class="hljs-keyword">then</span>
        <span class="hljs-comment"># Remove routes without metrics to avoid conflicts</span>
        <span class="hljs-built_in">local</span> no_metric_routes
        no_metric_routes=$(ip route show default | awk <span class="hljs-string">'!/metric/ {print}'</span>)
        <span class="hljs-keyword">if</span> [[ -n <span class="hljs-string">"<span class="hljs-variable">$no_metric_routes</span>"</span> ]]; <span class="hljs-keyword">then</span>
            <span class="hljs-built_in">echo</span> <span class="hljs-string">"<span class="hljs-variable">$no_metric_routes</span>"</span> | <span class="hljs-keyword">while</span> <span class="hljs-built_in">read</span> -r route; <span class="hljs-keyword">do</span>
                ip route del <span class="hljs-variable">$route</span> 2&gt;/dev/null || <span class="hljs-literal">true</span>
            <span class="hljs-keyword">done</span>
        <span class="hljs-keyword">fi</span>
        <span class="hljs-built_in">echo</span> <span class="hljs-string">"<span class="hljs-variable">$gateway</span>"</span>
    <span class="hljs-keyword">else</span>
        error <span class="hljs-string">"Failed to obtain gateway via DHCP"</span>
        <span class="hljs-built_in">return</span> 1
    <span class="hljs-keyword">fi</span>
}

<span class="hljs-comment"># Get current gateway with lowest metric</span>
<span class="hljs-function"><span class="hljs-title">get_current_gateway</span></span>() {
    ip route show default | \
        awk <span class="hljs-string">'BEGIN {min=10000} 
             {if ($0 ~ /default/ &amp;&amp; $NF+0 &lt; min) {min=$NF; gw=$3}} 
             END {print gw}'</span>
}

<span class="hljs-comment"># Test connectivity to target via specific interface</span>
<span class="hljs-function"><span class="hljs-title">test_connectivity</span></span>() {
    <span class="hljs-built_in">local</span> interface=<span class="hljs-string">"<span class="hljs-variable">$1</span>"</span>
    <span class="hljs-built_in">local</span> target=<span class="hljs-string">"<span class="hljs-variable">$2</span>"</span>

    ping -I <span class="hljs-string">"<span class="hljs-variable">$interface</span>"</span> -c 2 -W 1 <span class="hljs-string">"<span class="hljs-variable">$target</span>"</span> &amp;&gt;/dev/null
}

<span class="hljs-comment"># ============================================================================</span>
<span class="hljs-comment"># ROUTING MANAGEMENT FUNCTIONS</span>
<span class="hljs-comment"># ============================================================================</span>

<span class="hljs-comment"># Add specific route if it doesn't exist</span>
<span class="hljs-function"><span class="hljs-title">ensure_target_route</span></span>() {
    <span class="hljs-built_in">local</span> target=<span class="hljs-string">"<span class="hljs-variable">$1</span>"</span>
    <span class="hljs-built_in">local</span> gateway=<span class="hljs-string">"<span class="hljs-variable">$2</span>"</span> 
    <span class="hljs-built_in">local</span> interface=<span class="hljs-string">"<span class="hljs-variable">$3</span>"</span>

    <span class="hljs-built_in">local</span> current_route
    current_route=$(ip route get <span class="hljs-string">"<span class="hljs-variable">$target</span>"</span> 2&gt;/dev/null || <span class="hljs-literal">true</span>)

    <span class="hljs-keyword">if</span> ! <span class="hljs-built_in">echo</span> <span class="hljs-string">"<span class="hljs-variable">$current_route</span>"</span> | grep -q <span class="hljs-string">"dev <span class="hljs-variable">$interface</span>"</span>; <span class="hljs-keyword">then</span>
        info <span class="hljs-string">"Adding route to <span class="hljs-variable">$target</span> via <span class="hljs-variable">$interface</span>"</span>
        check_command ip route add <span class="hljs-string">"<span class="hljs-variable">$target</span>"</span> via <span class="hljs-string">"<span class="hljs-variable">$gateway</span>"</span> dev <span class="hljs-string">"<span class="hljs-variable">$interface</span>"</span>
    <span class="hljs-keyword">fi</span>
}

<span class="hljs-comment"># Flush all default routes and set new ones</span>
<span class="hljs-function"><span class="hljs-title">configure_routing</span></span>() {
    <span class="hljs-built_in">local</span> primary_gw=<span class="hljs-string">"<span class="hljs-variable">$1</span>"</span>
    <span class="hljs-built_in">local</span> primary_int=<span class="hljs-string">"<span class="hljs-variable">$2</span>"</span>
    <span class="hljs-built_in">local</span> secondary_gw=<span class="hljs-string">"<span class="hljs-variable">$3</span>"</span>
    <span class="hljs-built_in">local</span> secondary_int=<span class="hljs-string">"<span class="hljs-variable">$4</span>"</span>
    <span class="hljs-built_in">local</span> primary_metric=<span class="hljs-string">"<span class="hljs-variable">$5</span>"</span>
    <span class="hljs-built_in">local</span> secondary_metric=<span class="hljs-string">"<span class="hljs-variable">$6</span>"</span>

    info <span class="hljs-string">"Configuring routing with primary: <span class="hljs-variable">$primary_int</span>, secondary: <span class="hljs-variable">$secondary_int</span>"</span>

    <span class="hljs-comment"># Flush existing default routes [6]</span>
    ip route flush default

    <span class="hljs-comment"># Add primary route</span>
    info <span class="hljs-string">"Running: ip route add default via <span class="hljs-variable">$primary_gw</span> dev <span class="hljs-variable">$primary_int</span> metric <span class="hljs-variable">$primary_metric</span>"</span>
    check_command ip route add default via <span class="hljs-string">"<span class="hljs-variable">$primary_gw</span>"</span> dev <span class="hljs-string">"<span class="hljs-variable">$primary_int</span>"</span> metric <span class="hljs-string">"<span class="hljs-variable">$primary_metric</span>"</span>

    <span class="hljs-comment"># Add secondary route if available</span>
    <span class="hljs-keyword">if</span> [[ -n <span class="hljs-string">"<span class="hljs-variable">$secondary_gw</span>"</span> &amp;&amp; -n <span class="hljs-string">"<span class="hljs-variable">$secondary_int</span>"</span> ]]; <span class="hljs-keyword">then</span>
        check_command ip route add default via <span class="hljs-string">"<span class="hljs-variable">$secondary_gw</span>"</span> dev <span class="hljs-string">"<span class="hljs-variable">$secondary_int</span>"</span> metric <span class="hljs-string">"<span class="hljs-variable">$secondary_metric</span>"</span>
    <span class="hljs-keyword">fi</span>
}

<span class="hljs-comment"># ============================================================================</span>
<span class="hljs-comment"># NAT/IPTABLES MANAGEMENT FUNCTIONS</span>
<span class="hljs-comment"># ============================================================================</span>

<span class="hljs-comment"># Check if iptables rule exists [9]</span>
<span class="hljs-function"><span class="hljs-title">iptables_rule_exists</span></span>() {
    <span class="hljs-built_in">local</span> interface=<span class="hljs-string">"<span class="hljs-variable">$1</span>"</span>
    iptables -t nat -C POSTROUTING -s <span class="hljs-string">"<span class="hljs-variable">$NAT_SUBNET</span>"</span> -o <span class="hljs-string">"<span class="hljs-variable">$interface</span>"</span> -j MASQUERADE 2&gt;/dev/null
}

<span class="hljs-comment"># Remove iptables rule if it exists</span>
<span class="hljs-function"><span class="hljs-title">remove_iptables_rule</span></span>() {
    <span class="hljs-built_in">local</span> interface=<span class="hljs-string">"<span class="hljs-variable">$1</span>"</span>

    <span class="hljs-keyword">if</span> iptables_rule_exists <span class="hljs-string">"<span class="hljs-variable">$interface</span>"</span>; <span class="hljs-keyword">then</span>
        info <span class="hljs-string">"Removing NAT rule for <span class="hljs-variable">$interface</span>"</span>
        iptables -t nat -D POSTROUTING -s <span class="hljs-string">"<span class="hljs-variable">$NAT_SUBNET</span>"</span> -o <span class="hljs-string">"<span class="hljs-variable">$interface</span>"</span> -j MASQUERADE
    <span class="hljs-keyword">fi</span>
}

<span class="hljs-comment"># Add iptables masquerade rule</span>
<span class="hljs-function"><span class="hljs-title">add_iptables_rule</span></span>() {
    <span class="hljs-built_in">local</span> interface=<span class="hljs-string">"<span class="hljs-variable">$1</span>"</span>

    <span class="hljs-keyword">if</span> ! iptables_rule_exists <span class="hljs-string">"<span class="hljs-variable">$interface</span>"</span>; <span class="hljs-keyword">then</span>
        info <span class="hljs-string">"Adding NAT rule for <span class="hljs-variable">$interface</span>"</span>
        iptables -t nat -A POSTROUTING -s <span class="hljs-string">"<span class="hljs-variable">$NAT_SUBNET</span>"</span> -o <span class="hljs-string">"<span class="hljs-variable">$interface</span>"</span> -j MASQUERADE
    <span class="hljs-keyword">fi</span>
}

<span class="hljs-comment"># ============================================================================</span>
<span class="hljs-comment"># MAIN LOGIC FUNCTIONS</span>
<span class="hljs-comment"># ============================================================================</span>

<span class="hljs-comment"># Initialize secondary interface and get its gateway</span>
<span class="hljs-function"><span class="hljs-title">setup_secondary_interface</span></span>() {
    <span class="hljs-built_in">local</span> secondary_gw=<span class="hljs-string">""</span>

    <span class="hljs-comment"># if is_interface_available "$SECONDARY_INTERFACE"; then</span>
    <span class="hljs-comment">#     secondary_gw=$(get_interface_gateway "$SECONDARY_INTERFACE")</span>

    <span class="hljs-comment">#     info "Secondary Gateway: ${secondary_gw:-"Not found"}"</span>

    <span class="hljs-comment">#     if [[ -z "$secondary_gw" ]]; then</span>
    <span class="hljs-comment">#         secondary_gw=$(obtain_dhcp_gateway "$SECONDARY_INTERFACE" || echo "")</span>
    <span class="hljs-comment">#         if [[ -n "$secondary_gw" ]]; then</span>
    <span class="hljs-comment">#             info "Obtained secondary gateway: $secondary_gw"</span>
    <span class="hljs-comment">#         fi</span>
    <span class="hljs-comment">#     fi</span>
    <span class="hljs-comment"># else</span>
    <span class="hljs-comment">#     info "Secondary interface $SECONDARY_INTERFACE not found or not connected. Skipping secondary routing."</span>
    <span class="hljs-comment"># fi</span>

    <span class="hljs-comment"># echo "$secondary_gw"</span>
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"<span class="hljs-variable">$SECONDARY_GATEWAY_DEFAULT</span>"</span>
}

<span class="hljs-comment"># Handle primary interface routing and NAT</span>
<span class="hljs-function"><span class="hljs-title">configure_primary_interface</span></span>() {
    <span class="hljs-built_in">local</span> current_gw=<span class="hljs-string">"<span class="hljs-variable">$1</span>"</span>
    <span class="hljs-built_in">local</span> secondary_gw=<span class="hljs-string">"<span class="hljs-variable">$2</span>"</span>

    <span class="hljs-keyword">if</span> [[ <span class="hljs-string">"<span class="hljs-variable">$current_gw</span>"</span> != <span class="hljs-string">"<span class="hljs-variable">$PRIMARY_GATEWAY</span>"</span> ]]; <span class="hljs-keyword">then</span>
        info <span class="hljs-string">"Switching to: <span class="hljs-variable">$PRIMARY_INTERFACE</span>"</span>

        configure_routing <span class="hljs-string">"<span class="hljs-variable">$PRIMARY_GATEWAY</span>"</span> <span class="hljs-string">"<span class="hljs-variable">$PRIMARY_INTERFACE</span>"</span> \
                         <span class="hljs-string">"<span class="hljs-variable">$secondary_gw</span>"</span> <span class="hljs-string">"<span class="hljs-variable">$SECONDARY_INTERFACE</span>"</span> \
                         <span class="hljs-string">"<span class="hljs-variable">$PRIMARY_METRIC</span>"</span> <span class="hljs-string">"<span class="hljs-variable">$SECONDARY_METRIC</span>"</span>

        add_iptables_rule <span class="hljs-string">"<span class="hljs-variable">$PRIMARY_INTERFACE</span>"</span>
    <span class="hljs-keyword">else</span>
        info <span class="hljs-string">"Already using <span class="hljs-variable">$PRIMARY_INTERFACE</span> as default gateway."</span>
        <span class="hljs-keyword">if</span> ! iptables_rule_exists <span class="hljs-string">"<span class="hljs-variable">$PRIMARY_INTERFACE</span>"</span>; <span class="hljs-keyword">then</span>
            add_iptables_rule <span class="hljs-string">"<span class="hljs-variable">$PRIMARY_INTERFACE</span>"</span>
        <span class="hljs-keyword">fi</span>

        remove_iptables_rule <span class="hljs-string">"<span class="hljs-variable">$SECONDARY_INTERFACE</span>"</span>
    <span class="hljs-keyword">fi</span>
}

<span class="hljs-comment"># Handle secondary interface routing and NAT</span>
<span class="hljs-function"><span class="hljs-title">configure_secondary_interface</span></span>() {
    <span class="hljs-built_in">local</span> current_gw=<span class="hljs-string">"<span class="hljs-variable">$1</span>"</span>
    <span class="hljs-built_in">local</span> secondary_gw=<span class="hljs-string">"<span class="hljs-variable">$2</span>"</span>

    <span class="hljs-keyword">if</span> [[ -n <span class="hljs-string">"<span class="hljs-variable">$secondary_gw</span>"</span> &amp;&amp; <span class="hljs-string">"<span class="hljs-variable">$current_gw</span>"</span> != <span class="hljs-string">"<span class="hljs-variable">$secondary_gw</span>"</span> ]]; <span class="hljs-keyword">then</span>
        info <span class="hljs-string">"Switching to: <span class="hljs-variable">$SECONDARY_INTERFACE</span>"</span>

        configure_routing <span class="hljs-string">"<span class="hljs-variable">$secondary_gw</span>"</span> <span class="hljs-string">"<span class="hljs-variable">$SECONDARY_INTERFACE</span>"</span> \
                         <span class="hljs-string">"<span class="hljs-variable">$PRIMARY_GATEWAY</span>"</span> <span class="hljs-string">"<span class="hljs-variable">$PRIMARY_INTERFACE</span>"</span> \
                         <span class="hljs-string">"<span class="hljs-variable">$SECONDARY_METRIC</span>"</span> <span class="hljs-string">"<span class="hljs-subst">$((PRIMARY_METRIC + 20)</span>)"</span>

        add_iptables_rule <span class="hljs-string">"<span class="hljs-variable">$SECONDARY_INTERFACE</span>"</span>
    <span class="hljs-keyword">else</span>
        info <span class="hljs-string">"No working secondary interface or already using it as default gateway."</span>
        <span class="hljs-keyword">if</span> ! iptables_rule_exists <span class="hljs-string">"<span class="hljs-variable">$SECONDARY_INTERFACE</span>"</span>; <span class="hljs-keyword">then</span>
            add_iptables_rule <span class="hljs-string">"<span class="hljs-variable">$SECONDARY_INTERFACE</span>"</span>
        <span class="hljs-keyword">fi</span>

        remove_iptables_rule <span class="hljs-string">"<span class="hljs-variable">$PRIMARY_INTERFACE</span>"</span>
    <span class="hljs-keyword">fi</span>
}

<span class="hljs-comment"># Display current routing configuration</span>
<span class="hljs-function"><span class="hljs-title">show_routing_status</span></span>() {
    <span class="hljs-built_in">echo</span> <span class="hljs-string">""</span>
    info <span class="hljs-string">"Applied following settings:"</span>
    ip route show default
}

<span class="hljs-comment"># ============================================================================</span>
<span class="hljs-comment"># MAIN EXECUTION</span>
<span class="hljs-comment"># ============================================================================</span>

<span class="hljs-function"><span class="hljs-title">main</span></span>() {
    info <span class="hljs-string">"Starting network interface switching script"</span>

    <span class="hljs-comment"># Setup secondary interface</span>
    <span class="hljs-built_in">local</span> secondary_gw
    secondary_gw=$(setup_secondary_interface)

    <span class="hljs-comment"># Ensure target route exists via primary interface</span>
    ensure_target_route <span class="hljs-string">"<span class="hljs-variable">$TARGET_HOST</span>"</span> <span class="hljs-string">"<span class="hljs-variable">$PRIMARY_GATEWAY</span>"</span> <span class="hljs-string">"<span class="hljs-variable">$PRIMARY_INTERFACE</span>"</span>

    <span class="hljs-comment"># Get current gateway</span>
    <span class="hljs-built_in">local</span> current_gw
    current_gw=$(get_current_gateway)
    info <span class="hljs-string">"Current Gateway: <span class="hljs-variable">$current_gw</span>"</span>

    <span class="hljs-comment"># Test primary interface connectivity [8]</span>
    <span class="hljs-keyword">if</span> test_connectivity <span class="hljs-string">"<span class="hljs-variable">$PRIMARY_INTERFACE</span>"</span> <span class="hljs-string">"<span class="hljs-variable">$TARGET_HOST</span>"</span>; <span class="hljs-keyword">then</span>
        info <span class="hljs-string">"Target <span class="hljs-variable">$TARGET_HOST</span> is reachable via <span class="hljs-variable">$PRIMARY_INTERFACE</span>"</span>
        configure_primary_interface <span class="hljs-string">"<span class="hljs-variable">$current_gw</span>"</span> <span class="hljs-string">"<span class="hljs-variable">$secondary_gw</span>"</span>
    <span class="hljs-keyword">else</span>
        info <span class="hljs-string">"Target <span class="hljs-variable">$TARGET_HOST</span> is NOT reachable via <span class="hljs-variable">$PRIMARY_INTERFACE</span>"</span>
        configure_secondary_interface <span class="hljs-string">"<span class="hljs-variable">$current_gw</span>"</span> <span class="hljs-string">"<span class="hljs-variable">$secondary_gw</span>"</span>
    <span class="hljs-keyword">fi</span>

    show_routing_status
    info <span class="hljs-string">"Network interface switching completed successfully"</span>
}

<span class="hljs-comment"># Execute main function if script is run directly</span>
<span class="hljs-keyword">if</span> [[ <span class="hljs-string">"<span class="hljs-variable">${BASH_SOURCE[0]}</span>"</span> == <span class="hljs-string">"<span class="hljs-variable">${0}</span>"</span> ]]; <span class="hljs-keyword">then</span>
    main <span class="hljs-string">"<span class="hljs-variable">$@</span>"</span>
<span class="hljs-keyword">fi</span>
</code></pre>
<p>Also make sure it’s executable:</p>
<pre><code class="lang-bash">sudo chmod u+x /etc/keepalived/failover.sh
</code></pre>
<p>And that’s it — you’ve got a basic automated failover system ready to step in whenever your internet tries to betray you. Make sure to change variables at the top of the script according to your situation.</p>
<h3 id="heading-the-finale-spreading-the-internet-love">The Finale: Spreading the Internet Love</h3>
<p>Now that our failover setup is up and running like a responsible adult, I figured — why keep all this internet awesomeness to just one machine?<br />Let’s share it with the rest of the devices in the house!<br />(You're welcome, future smart bulbs, fridges, and random IoT doodads.)</p>
<p>This final part is where we build a virtual network that bridges a physical LAN port on my Ubuntu machine, so I can hook it up to a second router and spread that sweet, sweet internet.</p>
<h3 id="heading-step-1-time-to-mess-with-netplan">Step 1: Time to mess with netplan</h3>
<p>Ubuntu uses <strong>netplan</strong> for its network configs — basically just YAML files with feelings.<br />Let’s create our virtual bridge network.</p>
<p>Fire up your editor:</p>
<pre><code class="lang-bash">sudo nano /etc/netplan/01-bridge.yaml
</code></pre>
<p>And paste in:</p>
<pre><code class="lang-bash">network:
  version: 2
  ethernets:
    eno0:
      dhcp4: <span class="hljs-literal">false</span>
    wlp3s0:
      dhcp4: <span class="hljs-literal">true</span>
    usbnet0:
      dhcp4: <span class="hljs-literal">true</span>
  bridges:
    vmbr0:
      addresses:
      - <span class="hljs-string">"10.10.2.1/24"</span>
      dhcp4: <span class="hljs-literal">false</span>
      interfaces:
      - eno0
      parameters:
        forward-delay: <span class="hljs-string">"0"</span>
        stp: <span class="hljs-literal">false</span>
</code></pre>
<p>After that:</p>
<pre><code class="lang-bash">sudo netplan apply
</code></pre>
<h3 id="heading-step-2-what-this-actually-does">Step 2: What this actually does</h3>
<ul>
<li><p>Creates a bridge called <code>vmbr0</code> using the subnet <code>10.10.2.0/24</code></p>
</li>
<li><p>Bridges it to <code>eno0</code> — that’s my physical LAN port</p>
</li>
<li><p>No DHCP server is running on my Ubuntu machine — we’re keeping it simple (and very manual)</p>
</li>
</ul>
<h3 id="heading-step-3-connecting-devices">Step 3: Connecting devices</h3>
<p>Since there’s no DHCP, any device you connect to <code>eno0</code> will need to be set up with a <strong>static IP</strong>. For example:</p>
<ul>
<li><p>IP: <code>10.10.2.2</code></p>
</li>
<li><p>Subnet mask: <code>255.255.255.0</code></p>
</li>
<li><p>Gateway: <code>10.10.2.1</code> (your Ubuntu machine)</p>
</li>
</ul>
<p>And that’s it — your Ubuntu box is now basically moonlighting as a mini-router for your whole house (or lab, or dungeon — I’m not judging).</p>
]]></content:encoded></item><item><title><![CDATA[Guide: How to configure Proxmox with WiFi]]></title><description><![CDATA[So, one day I saw my old laptop sitting there, collecting dust like it was auditioning for a role in an apocalypse movie. And then it hit me: "Why not turn it into a server?" There was just one minor hiccup. Its Ethernet port was as dead as my dreams...]]></description><link>https://blog.vivekkaushik.com/guide-how-to-configure-proxmox-with-wifi</link><guid isPermaLink="true">https://blog.vivekkaushik.com/guide-how-to-configure-proxmox-with-wifi</guid><category><![CDATA[proxmox]]></category><category><![CDATA[wifi]]></category><category><![CDATA[networking]]></category><category><![CDATA[Linux]]></category><category><![CDATA[dns]]></category><dc:creator><![CDATA[Vivek Kaushik]]></dc:creator><pubDate>Tue, 31 Dec 2024 18:06:20 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1735664040078/a5455883-992b-46c6-8624-027f2cdcdea7.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>So, one day I saw my old laptop sitting there, collecting dust like it was auditioning for a role in an apocalypse movie. And then it hit me: "Why not turn it into a server?" There was just one minor hiccup. Its Ethernet port was as dead as my dreams of becoming a professional guitarist. But hey, the WiFi was still kicking! After hours scouring the internet and performing digital sorcery, I made it work. Here's a guide for anyone crazy enough to try the same.</p>
<h3 id="heading-step-1-configure-wifi-credentials">Step 1: Configure WiFi Credentials</h3>
<p>First things first—let's get that server connected to WiFi. Use the following magical incantation:</p>
<pre><code class="lang-bash">wpa_passphrase SSIDNAME PASSWORD &gt;&gt; /etc/wpa_supplicant/wpa_supplicant.conf
</code></pre>
<p>Replace <code>SSIDNAME</code> and <code>PASSWORD</code> with your network's details. Then, edit the resulting file at <code>/etc/wpa_supplicant/wpa_supplicant.conf</code> to look something like this:</p>
<pre><code class="lang-apache"><span class="hljs-attribute">ctrl_interface</span>=/run/wpa_supplicant
<span class="hljs-attribute">update_config</span>=<span class="hljs-number">1</span>
<span class="hljs-attribute">country</span>=IN

<span class="hljs-attribute">network</span>={
        <span class="hljs-attribute">ssid</span>=<span class="hljs-string">"SSID_NAME"</span>
        <span class="hljs-comment">#psk="asdflasf"</span>
        <span class="hljs-attribute">psk</span>=<span class="hljs-number">89</span>d<span class="hljs-number">64643245</span>wsfdfg<span class="hljs-number">237</span>ed<span class="hljs-number">3</span>ec<span class="hljs-number">3</span>ef<span class="hljs-number">31</span>c<span class="hljs-number">47</span>e<span class="hljs-number">75430</span>f<span class="hljs-number">0</span>asdfji<span class="hljs-number">40598</span>
        <span class="hljs-attribute">proto</span>=WPA RSN
        <span class="hljs-attribute">key_mgmt</span>=WPA-PSK
}
</code></pre>
<p>Pro tip: That <code>#psk="your_password_in_plain_text"</code> line is commented out for a reason. Don't let your server broadcast your WiFi password to the world!</p>
<h3 id="heading-step-2-configure-network-interfaces">Step 2: Configure Network Interfaces</h3>
<p>Next up, let’s tackle the network interfaces. Open your favorite text editor (or <code>nano</code>, if you must) and edit <code>/etc/network/interfaces</code> like so:</p>
<pre><code class="lang-apache"><span class="hljs-attribute">auto</span> lo
<span class="hljs-attribute">iface</span> lo inet loopback

<span class="hljs-comment"># Wifi interface autoconnect using wpa_supplicant.conf</span>
<span class="hljs-attribute">auto</span> wlp<span class="hljs-number">3</span>s<span class="hljs-number">0</span>
<span class="hljs-attribute">iface</span> wlp<span class="hljs-number">3</span>s<span class="hljs-number">0</span> inet dhcp
        <span class="hljs-attribute">wpa</span>-conf /etc/wpa_supplicant/wpa_supplicant.conf

<span class="hljs-comment"># Virtual bridge network</span>
<span class="hljs-attribute">auto</span> vmbr<span class="hljs-number">0</span>
<span class="hljs-attribute">iface</span> vmbr<span class="hljs-number">0</span> inet static
        <span class="hljs-attribute">address</span> <span class="hljs-number">10.1.1.1</span>/<span class="hljs-number">24</span>
        <span class="hljs-attribute">bridge</span>-ports none
        <span class="hljs-attribute">bridge</span>-stp <span class="hljs-literal">off</span>
        <span class="hljs-attribute">bridge</span>-fd <span class="hljs-number">0</span>

        <span class="hljs-attribute">post</span>-up echo <span class="hljs-number">1</span> &gt; /proc/sys/net/ipv<span class="hljs-number">4</span>/ip_forward
        <span class="hljs-attribute">post</span>-up iptables -t nat -A POSTROUTING -s '<span class="hljs-number">10.1.1.0</span>/<span class="hljs-number">24</span>' -o wlp<span class="hljs-number">3</span>s<span class="hljs-number">0</span> -j MASQUERADE
        <span class="hljs-attribute">post</span>-down iptables -t nat -D POSTROUTING -s '<span class="hljs-number">10.1.1.0</span>/<span class="hljs-number">24</span>' -o wlp<span class="hljs-number">3</span>s<span class="hljs-number">0</span> -j MASQUERADE
        <span class="hljs-attribute">post</span>-up iptables -t raw -I PREROUTING -i fwbr+ -j CT --zone <span class="hljs-number">1</span>
        <span class="hljs-attribute">post</span>-down iptables -t raw -D PREROUTING -i fwbr+ -j CT --zone <span class="hljs-number">1</span>

<span class="hljs-attribute">source</span> /etc/network/interfaces.d/*
</code></pre>
<p><strong>Heads up:</strong> Your WiFi interface might not be called <code>wlp3s0</code>. Use the <code>ip a</code> command to find out what it’s really called. Also, if you want to use a different subnet, replace <code>10.1.1.0/24</code> with your preferred network range.</p>
<p>If you’re feeling fancy, you can even change your MAC address with the following configuration:</p>
<pre><code class="lang-apache"><span class="hljs-attribute">iface</span> wlp<span class="hljs-number">3</span>s<span class="hljs-number">0</span> inet static
        <span class="hljs-attribute">address</span> <span class="hljs-number">192.168.1.2</span>
        <span class="hljs-attribute">netmask</span> <span class="hljs-number">255.255.255.0</span>
        <span class="hljs-attribute">gateway</span> <span class="hljs-number">192.168.1.1</span>
        <span class="hljs-attribute">dns</span>-nameservers <span class="hljs-number">1.1.1.1</span> <span class="hljs-number">1.0.0.1</span>
        <span class="hljs-attribute">hardware</span> ether <span class="hljs-number">06</span>:<span class="hljs-number">61</span>:D<span class="hljs-number">1</span>:<span class="hljs-number">65</span>:<span class="hljs-number">61</span>:<span class="hljs-number">64</span>
        <span class="hljs-attribute">wpa</span>-conf /etc/wpa_supplicant/wpa_supplicant.conf
</code></pre>
<p>When you’re done, restart the networking service to apply your changes:</p>
<pre><code class="lang-bash">systemctl restart networking
</code></pre>
<h3 id="heading-step-3-install-and-configure-dnsmasq">Step 3: Install and Configure <code>dnsmasq</code></h3>
<p>Now, let’s set up a DHCP server to dish out IP addresses to your VMs connected to <code>vmbr0</code>. Start by installing <code>dnsmasq</code>:</p>
<pre><code class="lang-bash">apt install dnsmasq
</code></pre>
<p>Then, edit its configuration file <code>/etc/dnsmasq.conf</code> with the following lines:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Set the interface where you want DHCP server to assign IP</span>
interface=vmbr0

<span class="hljs-comment"># DHCP address range</span>
dhcp-range=10.1.1.1,10.1.1.255,255.255.255.0,12h
</code></pre>
<p>Restart <code>dnsmasq</code> to apply the changes:</p>
<pre><code class="lang-bash">systemctl restart dnsmasq
</code></pre>
<h3 id="heading-conclusion">Conclusion</h3>
<p>Congratulations, you’ve successfully turned a dusty old laptop with a broken Ethernet port into a WiFi-powered homelab server!</p>
<h3 id="heading-bonus-configure-lid-configuration">Bonus: Configure Lid Configuration</h3>
<p>If you’re also using a laptop for this, you might want to keep it going even if the lid closes! Edit the <code>/etc/systemd/logind.conf</code> file to make sure it stays alive even when you close the lid:</p>
<pre><code class="lang-bash">HandleSuspendKey=ignore
HandleSuspendKeyLongPress=ignore

HandleLidSwitch=ignore
HandleLidSwitchExternalPower=ignore
HandleLidSwitchDocked=ignore

LidSwitchIgnoreInhibited=no
</code></pre>
<p>After updating the above file with the values provided restart `logind`:</p>
<pre><code class="lang-bash">systemctl restart systemd-logind
</code></pre>
<h3 id="heading-one-more-thing">One More Thing!</h3>
<p>With this setup, your virtual machines have full access to your router’s network. However, devices directly connected to the router won’t know how to reach the network you just defined. To fix this, you’ll need to create a route entry in your router’s or device’s routing table.</p>
<p>Since router interfaces vary wildly, here’s how you can do it on a Mac:</p>
<pre><code class="lang-bash">sudo route -n add 10.1.1.0/24 192.168.1.2
</code></pre>
<p>Just replace <code>10.1.1.0/24</code> with your subnet and <code>192.168.1.2</code> with your homelab server’s IP address. In case you want to remove it use:</p>
<pre><code class="lang-bash">sudo route -n delete 10.1.1.0/24 192.168.1.2
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Step-by-Step Guide: Nginx Ingress Setup on AKS]]></title><description><![CDATA[Recently I was tasked with setting up kubernetes cluster with an Nginx Ingress. As a newcomer to Kubernetes, I tackled this setup and learned a thing or two along the way. Let me walk you through my experience and share some helpful tips.
The Goal: S...]]></description><link>https://blog.vivekkaushik.com/step-by-step-guide-nginx-ingress-setup-on-aks</link><guid isPermaLink="true">https://blog.vivekkaushik.com/step-by-step-guide-nginx-ingress-setup-on-aks</guid><dc:creator><![CDATA[Vivek Kaushik]]></dc:creator><pubDate>Wed, 07 Aug 2024 19:19:54 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1723011750409/aa9a8b7a-d25e-4843-9e1a-68b00990d30b.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Recently I was tasked with setting up kubernetes cluster with an Nginx Ingress. As a newcomer to Kubernetes, I tackled this setup and learned a thing or two along the way. Let me walk you through my experience and share some helpful tips.</p>
<p><strong>The Goal:</strong> Set up a Kubernetes Cluster with Nginx Ingress</p>
<p>Here's where things got interesting. After setting up my cluster, I hit a snag: I couldn't get a public IP assigned to my Nginx ingress. All I got was a private IP, which wasn't very useful for my needs.</p>
<p>After some extensive searching (and maybe a few sighs of frustration), I finally found the solution in the Nginx documentation (<a target="_blank" href="https://kubernetes.github.io/ingress-nginx/deploy/">https://kubernetes.github.io/ingress-nginx/deploy/</a>). What a relief!</p>
<p>Before We Start...</p>
<p>Let's make sure we're on the same page. This guide assumes you've already configured your K8s cluster on kubectl. If you haven't, no problem! Just use <code>az login</code> to access your Azure account and use the connect command from your Azure AKS cluster. It's pretty straightforward.</p>
<h3 id="heading-the-key-command">The Key Command</h3>
<p>Here's the command that'll set up your Nginx ingress controller:</p>
<pre><code class="lang-bash">kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.11.1/deploy/static/provider/cloud/deploy.yaml
</code></pre>
<p>Found this gem after going throught the documentation (maybe we should read it sometime) <a target="_blank" href="https://kubernetes.github.io/ingress-nginx/deploy/#azure">https://kubernetes.github.io/ingress-nginx/deploy/#azure</a>.</p>
<h3 id="heading-create-a-certificate-secret">Create a certificate Secret</h3>
<p>Now, let's tackle the next exciting step in our K8s journey: creating a certificate Secret! 🔐.</p>
<p>You'll need to whip up a certificate for your domain. You've got two options here:</p>
<ol>
<li><p>Snag a free certificate from Let's Encrypt</p>
</li>
<li><p>Purchase one from a certificate authority (fancy, I know!)</p>
</li>
</ol>
<p>Once you've got your hands on that shiny certificate and key, Run this command:</p>
<pre><code class="lang-bash">kubectl create secret tls tls-conf --cert /path/to/fullchain.pem --key /path/to/privkey.pem --namespace your-namespace
</code></pre>
<p>Don't forget to swap out the <code>path</code> and <code>namespace</code> with your actual values. This command creates a secret object of type <code>kubernetes.io/tls</code>.</p>
<h3 id="heading-time-to-create-a-ingress">Time to create a Ingress</h3>
<p>Next up, we're creating an Ingress for your service. Think of it as the bouncer that decides who gets into your Kubernetes club.</p>
<p>Create a file called <code>ingress.yaml</code> and fill it with this yaml goodness:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">networking.k8s.io/v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Ingress</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">ingress-name</span>
  <span class="hljs-attr">namespace:</span> <span class="hljs-string">your-namespace</span>
  <span class="hljs-attr">annotations:</span>
    <span class="hljs-attr">nginx.ingress.kubernetes.io/ssl-redirect:</span> <span class="hljs-string">"true"</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">tls:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">hosts:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">your-domain.something</span>
      <span class="hljs-attr">secretName:</span> <span class="hljs-string">tls-conf</span>
  <span class="hljs-attr">rules:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">host:</span> <span class="hljs-string">your-domain.something</span>
      <span class="hljs-attr">http:</span>
        <span class="hljs-attr">paths:</span>
          <span class="hljs-bullet">-</span> <span class="hljs-attr">path:</span> <span class="hljs-string">/</span>
            <span class="hljs-attr">pathType:</span> <span class="hljs-string">Prefix</span>
            <span class="hljs-attr">backend:</span>
              <span class="hljs-attr">service:</span>
                <span class="hljs-attr">name:</span> <span class="hljs-string">your-service</span>
                <span class="hljs-attr">port:</span>
                  <span class="hljs-attr">number:</span> <span class="hljs-string">your-service-port</span>
  <span class="hljs-attr">ingressClassName:</span> <span class="hljs-string">nginx</span>
</code></pre>
<p>Now, before you apply this yaml, make sure to replace <code>your-namespace</code>, <code>your-domain.something</code>, <code>your-service</code>, and <code>your-service-port</code> with your actual values.</p>
<p>Once you've customized your yaml, apply it using kubectl. Voila! You've just set up an Ingress for your service using the following command.</p>
<pre><code class="lang-bash">kubectl apply -f /path/to/ingress.yaml
</code></pre>
<p>Replace the <code>/path/to/ingress.yaml</code> with wherever you saved your ingress.yaml.</p>
<p>And that's it you should have your nginx ingress up and running with a public IP assigned to it.</p>
<h3 id="heading-bonus-round-customizing-your-nginx-config">Bonus Round: Customizing Your Nginx Config 🛠️</h3>
<p>What if you want to tweak some Nginx settings? Maybe you're looking to adjust the <code>client_max_body_size</code> or other fancy parameters? Well, you're in luck!</p>
<ol>
<li><p>Head over to the <code>config</code> objects in your Kubernetes dashboard.</p>
</li>
<li><p>Filter by the <code>ingress-nginx</code> namespace.</p>
</li>
<li><p>find and edit th <code>ingress-nginx-controller</code></p>
</li>
<li><p>Once you're happy with your edits, apply them. A complete list of available configuration options can be found at: <a target="_blank" href="https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/">https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/</a>.</p>
</li>
</ol>
]]></content:encoded></item><item><title><![CDATA[Configure qBittorrent on Dokku and Mount SMB NAS for Download Storage]]></title><description><![CDATA[Ready to set up qBittorrent on Dokku? Follow these steps, and you'll be up and running in no time! (This article assumes you have dokku and a nas server already up and running.)
Step 1: Create a New App
First, create your new app in Dokku with the fo...]]></description><link>https://blog.vivekkaushik.com/configure-qbittorrent-on-dokku-and-mount-smb-nas-for-download-storage</link><guid isPermaLink="true">https://blog.vivekkaushik.com/configure-qbittorrent-on-dokku-and-mount-smb-nas-for-download-storage</guid><category><![CDATA[Dokku]]></category><category><![CDATA[qBittorrent]]></category><category><![CDATA[Torrent]]></category><category><![CDATA[nas]]></category><category><![CDATA[SMB]]></category><dc:creator><![CDATA[Vivek Kaushik]]></dc:creator><pubDate>Sun, 19 May 2024 09:55:23 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1716112355652/0bfc8576-859c-4f78-8722-1394f9df5211.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Ready to set up qBittorrent on Dokku? Follow these steps, and you'll be up and running in no time! (This article assumes you have dokku and a nas server already up and running.)</p>
<h4 id="heading-step-1-create-a-new-app">Step 1: Create a New App</h4>
<p>First, create your new app in Dokku with the following command:</p>
<pre><code class="lang-bash">dokku apps:create qbittorrent
</code></pre>
<p>Feel free to replace <code>qbittorrent</code> with your preferred app name.</p>
<h4 id="heading-step-2-mount-a-directory-for-qbittorrent-configuration">Step 2: Mount a Directory for qBittorrent Configuration</h4>
<p>Next, mount a directory for qBittorrent's configuration:</p>
<pre><code class="lang-bash">dokku storage:mount qbittorrent /home/dokku/qbittorrent/data:/config
</code></pre>
<h4 id="heading-step-3-create-a-docker-volume">Step 3: Create a Docker Volume</h4>
<p>Now, let's create a volume. Run the following command, replacing the placeholders with your specific values:</p>
<pre><code class="lang-bash">docker volume create --driver <span class="hljs-built_in">local</span> --opt <span class="hljs-built_in">type</span>=cifs --opt device=//&lt;nas_url_here&gt;/&lt;nas_path&gt; --opt o=username=&lt;your_username&gt;,password=&lt;your_password&gt;,port=&lt;your_port&gt;,uid=1000,gid=1000,forceuid &lt;name_of_the_volume&gt;
</code></pre>
<p>Replace <code>&lt;nas_url_here&gt;</code>, <code>&lt;nas_path&gt;</code>, <code>&lt;your_username&gt;</code>, <code>&lt;your_password&gt;</code>, <code>&lt;your_port&gt;</code>, and <code>&lt;name_of_the_volume&gt;</code> with your actual NAS details and desired volume name.</p>
<h4 id="heading-step-4-mount-the-volume">Step 4: Mount the Volume</h4>
<p>Mount the newly created volume:</p>
<pre><code class="lang-bash">dokku storage:mount qbittorrent &lt;name_of_the_volume&gt;:/downloads
</code></pre>
<p>Ensure you replace <code>&lt;name_of_the_volume&gt;</code> with the volume name you used earlier.</p>
<h4 id="heading-step-5-set-environment-variables">Step 5: Set Environment Variables</h4>
<p>Configure the environment variables for your app:</p>
<pre><code class="lang-bash">dokku config:<span class="hljs-built_in">set</span> qbittorrent PGID=1000 PUID=1000 TZ=Asia/Kolkata WEBUI_PORT=8080
</code></pre>
<h4 id="heading-step-6-deploy-the-docker-image">Step 6: Deploy the Docker Image</h4>
<p>Initiate the deployment using the docker image:</p>
<pre><code class="lang-bash">dokku git:from-image qbittorrent linuxserver/qbittorrent:4.6.0
</code></pre>
<h4 id="heading-step-7-configure-ports">Step 7: Configure Ports</h4>
<p>Set up the port configuration to make your app accessible via Dokku's proxy server:</p>
<pre><code class="lang-bash">dokku ports:<span class="hljs-built_in">set</span> qbittorrent http:80:8080
</code></pre>
<h4 id="heading-step-8-set-a-domain">Step 8: Set a Domain</h4>
<p>Finally, set a domain for your app:</p>
<pre><code class="lang-bash">dokku domains:add qbittorrent &lt;your_domain_here&gt;
</code></pre>
<p>Replace <code>&lt;your_domain_here&gt;</code> with your actual domain.</p>
<hr />
<p>And that's it! Your qBittorrent app should now be up and running on Dokku. Happy torrenting!</p>
]]></content:encoded></item><item><title><![CDATA[Bring your app online using SSH with a custom domain for free]]></title><description><![CDATA[If you're a developer you must've encountered a situation where localhost just wouldn't work for you, maybe you wanted to show off your project to someone else or needed to set up a public URL for a webhook. There are countless situations where you w...]]></description><link>https://blog.vivekkaushik.com/bring-your-app-online-using-ssh-with-a-custom-domain-for-free</link><guid isPermaLink="true">https://blog.vivekkaushik.com/bring-your-app-online-using-ssh-with-a-custom-domain-for-free</guid><category><![CDATA[ssh]]></category><category><![CDATA[Linux]]></category><category><![CDATA[proxy]]></category><category><![CDATA[ngrok]]></category><dc:creator><![CDATA[Vivek Kaushik]]></dc:creator><pubDate>Sun, 29 Jan 2023 20:57:39 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1675022773267/4e9fd6d5-ca31-40c3-a6c8-262386e37f3a.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If you're a developer you must've encountered a situation where localhost just wouldn't work for you, maybe you wanted to show off your project to someone else or needed to set up a public URL for a webhook. There are countless situations where you want your project to be accessible from the internet while it is still in the development phase.</p>
<p>What if I told you there is a way to do this with tools you're already using?</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1675023961031/28aad354-957a-4d42-b4e9-1679c6d9cf15.gif" alt class="image--center mx-auto" /></p>
<p>Yeah, you read that right, you can bring your application online using SSH.</p>
<p>Why go through the effort when there are free third-party services like ngrok?</p>
<p>The problem with ngrok or any third-party services is that they don't provide a dedicated domain, meaning every time you restart your ngrok client it will give you a different domain and you'll have to change the configuration all over again, in all fairness you could buy a paid plan to have a custom domain setup as well but let's try it my way.</p>
<p>Before we begin, as the title suggests this is not entirely free. I know I said free and it is to some extent, but it is only free if you're already having a domain and a server. A domain may not be a fancy one but can be acquired for free from <a target="_blank" href="https://www.freenom.com/en/index.html?lang=en">Freenom</a>. And there are free tiers available from different cloud providers.</p>
<h3 id="heading-setting-up-nginx-for-reverse-proxy">Setting up Nginx for reverse proxy</h3>
<p>Once you have got a server up and running in the cloud, ssh into the server and install Nginx.</p>
<pre><code class="lang-bash">sudo apt install nginx
</code></pre>
<p>After the successful installation head into the <code>/etc/nginx/sites-avaialble</code> directory and create a new server configuration using <code>sudo touch tunnel.conf</code> then add the following configuration.</p>
<pre><code class="lang-bash">server {
    listen 80;
    listen [::]:80;

    server_name tunnel.yourdomain.com;

    location / {
      proxy_pass http://localhost:8080/;
      proxy_set_header Accept-Encoding gzip;
    }
}
</code></pre>
<p>replace <code>tunnel.yourdomain.com</code> with your domain. Also, add <code>A record</code> that points to your server IP for the same.</p>
<p>You can also use <a target="_blank" href="https://certbot.eff.org/">Certbot</a> to get an SSL certificate for your domain.</p>
<h3 id="heading-configure-ssh-client-to-port-forward">Configure SSH client to port forward</h3>
<p>Now that nginx is listening on the server, every time you want to bring your application online just run the following command and access it from your specified domain.</p>
<pre><code class="lang-bash">
ssh -R remote_port:host:host_port -i ~/.ssh/private_key user@yourserver.com
</code></pre>
<p>change the <code>remote_port</code> to <code>8080</code>, <code>host</code> to <code>localhost</code> &amp; <code>host_port</code> to whatever your application is listening on.</p>
<p>Also, your ssh connection may get disconnected if there is no activity in a while, to keep the connection alive add <code>ServerAliveInterval 60</code> at the end of <code>~/.ssh/config</code> file.</p>
]]></content:encoded></item><item><title><![CDATA[Publishing Flutter apps to Play Store using Github Actions and Fastlane]]></title><description><![CDATA[Recently I was trying to figure out how I can publish flutter apps to the Play Store using Github Actions and publish them I did. I started reading some documentation available at flutter's official site and followed the instructions to set up Fastla...]]></description><link>https://blog.vivekkaushik.com/publishing-flutter-apps-to-play-store-using-github-actions-and-fastlane</link><guid isPermaLink="true">https://blog.vivekkaushik.com/publishing-flutter-apps-to-play-store-using-github-actions-and-fastlane</guid><category><![CDATA[Flutter]]></category><category><![CDATA[github-actions]]></category><category><![CDATA[continuous deployment]]></category><category><![CDATA[Playstore]]></category><category><![CDATA[fastlane]]></category><dc:creator><![CDATA[Vivek Kaushik]]></dc:creator><pubDate>Mon, 29 Aug 2022 05:07:50 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1661617603168/HAu97nbjt.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Recently I was trying to figure out how I can publish flutter apps to the Play Store using Github Actions and publish them I did. I started reading some documentation available at <a target="_blank" href="https://docs.flutter.dev/deployment/cd">flutter's official site</a> and followed the instructions to set up Fastlane locally on my machine.  First, we will get the Fastlane working on your local machine.</p>
<h4 id="heading-installing-fastlane">Installing Fastlane</h4>
<p>First, we will install the Fastlane tool on our local machine. Use homebrew to install it on a mac</p>
<pre><code><span class="hljs-attribute">brew</span> install fastlane
</code></pre><p>on a Linux or Windows machine, we'll use ruby's gem package manager to install Fastlane.</p>
<pre><code><span class="hljs-attribute">gem</span> install fastlane
</code></pre><h4 id="heading-setting-up-app-signing">Setting up app signing</h4>
<p>Go to <code>android/app/build.gradle</code> file and define add the following lines:</p>
<pre><code>...
<span class="hljs-comment">// Add these lines</span>
def keystoreProperties <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> Properties()
def keystorePropertiesFile <span class="hljs-operator">=</span> rootProject.file(<span class="hljs-string">'key.properties'</span>)
<span class="hljs-keyword">if</span> (keystorePropertiesFile.exists()) {
    keystoreProperties.load(<span class="hljs-keyword">new</span> FileInputStream(keystorePropertiesFile))
}

<span class="hljs-comment">// ^ Above this</span>
apply plugin: <span class="hljs-string">'com.android.application'</span>
apply plugin: <span class="hljs-string">'com.google.gms.google-services'</span>

...

 defaultConfig {
    ...

    <span class="hljs-comment">// Add this as well</span>
    signingConfigs {
        release {
            keyAlias keystoreProperties[<span class="hljs-string">'keyAlias'</span>]
            keyPassword keystoreProperties[<span class="hljs-string">'keyPassword'</span>]
            storeFile keystoreProperties[<span class="hljs-string">'storeFile'</span>] ? file(keystoreProperties[<span class="hljs-string">'storeFile'</span>]) : null
            storePassword keystoreProperties[<span class="hljs-string">'storePassword'</span>]
        }
    }

    buildTypes {
        release {
            <span class="hljs-comment">// <span class="hljs-doctag">TODO:</span> Add your own signing config for the release build.</span>
            <span class="hljs-comment">// Signing with the debug keys for now, so `flutter run --release` works.</span>
            signingConfig signingConfigs.release
        }

    }

...
</code></pre><p>You also need to create a new file under the android folder called <code>key.properties</code>, and add the following configurations:</p>
<pre><code>storePassword<span class="hljs-operator">=</span>store<span class="hljs-operator">-</span>password
keyPassword<span class="hljs-operator">=</span>key<span class="hljs-operator">-</span>password
keyAlias<span class="hljs-operator">=</span>key<span class="hljs-operator">-</span>alias
storeFile<span class="hljs-operator">=</span>path<span class="hljs-operator">-</span>to<span class="hljs-operator">-</span>jks
</code></pre><p>make sure to change these with your Keystore configuration. Run the <code>flutter build appbundle</code> command to build a signed app bundle.</p>
<h4 id="heading-creating-the-service-account">Creating the service account</h4>
<p>To upload the app bundle on the play console we need a service account that will have the required permissions to perform actions on our behalf. Before doing this make sure you have already registered an app on the play console with the correct package name.</p>
<ul>
<li>Head over to <a target="_blank" href="play.google.com/console">Play Console</a>.</li>
<li>On the left sidebar under <strong>Setup</strong>, you'll see <strong>API Access</strong>.</li>
<li>Scroll Down until you see the <strong>Service Accounts</strong> section.</li>
<li>Click on <strong>Learn how to create service accounts</strong>.</li>
<li>On the popup that shows up click on the link that says <strong>Google Cloud Platform</strong></li>
<li>Once you're in the google cloud console, click on <strong>Create service account</strong></li>
<li>This will ask you a couple of questions, just fill those in and create a new service account.</li>
<li>Once a new service account is created click on the <strong>three dots</strong> for the context menu and then <strong>Manage Keys</strong></li>
<li>Now you need to create a JSON key for this service account.</li>
<li>Once all that is done return back to the play console and click on the <strong>Done</strong> button in the Dialog Box.</li>
<li>You should see the service account listed on the play console. Click on the <strong>View Play Console Permissions</strong>.</li>
<li>Under app permission, you need to give admin access to this account for the app you want it to manage.</li>
</ul>
<h4 id="heading-setting-up-fastlane-for-the-project">Setting up Fastlane for the project</h4>
<p> Now that app signing is set up you need to configure Fastlane for the android project. Make sure you are in the android folder and run the following command:</p>
<pre><code>fastlane <span class="hljs-keyword">init</span>
</code></pre><p>Fastlane will ask you a couple of questions, like what's the package name for your app and path to the json secret (this is the file that we downloaded from the last step).</p>
<p>This will create a new folder called <code>fastlane</code>, which will contain two files <code>Appfile</code> and <code>Fastfile</code>. Make sure the package name and path to JSON secret are correct in the Appfile. </p>
<p>Open the <code>Fastfile</code> and replace it with the following lines:</p>
<pre><code><span class="hljs-comment"># This file contains the fastlane.tools configuration</span>
<span class="hljs-comment"># You can find the documentation at https://docs.fastlane.tools</span>
<span class="hljs-comment">#</span>
<span class="hljs-comment"># For a list of all available actions, check out</span>
<span class="hljs-comment">#</span>
<span class="hljs-comment">#     https://docs.fastlane.tools/actions</span>
<span class="hljs-comment">#</span>
<span class="hljs-comment"># For a list of all available plugins, check out</span>
<span class="hljs-comment">#</span>
<span class="hljs-comment">#     https://docs.fastlane.tools/plugins/available-plugins</span>
<span class="hljs-comment">#</span>

<span class="hljs-comment"># Uncomment the line if you want fastlane to automatically update itself</span>
<span class="hljs-comment"># update_fastlane</span>

default_platform(<span class="hljs-symbol">:android</span>)

platform <span class="hljs-symbol">:android</span> <span class="hljs-keyword">do</span>
  desc <span class="hljs-string">"Submit a new Google Play [Beta Testing]"</span>
  lane <span class="hljs-symbol">:beta</span> <span class="hljs-keyword">do</span>
    upload_to_play_store(
      <span class="hljs-symbol">track:</span> <span class="hljs-string">'internal'</span>,
      <span class="hljs-symbol">release_status:</span> <span class="hljs-string">'draft'</span>,
      <span class="hljs-symbol">aab:</span> <span class="hljs-string">'../build/app/outputs/bundle/release/app-release.aab'</span>
    )

    <span class="hljs-comment"># sh "your_script.sh"</span>
    <span class="hljs-comment"># You can also use other beta testing services here</span>
  <span class="hljs-keyword">end</span>

  desc <span class="hljs-string">"Deploy a new version to the Google Play [Production]"</span>
  lane <span class="hljs-symbol">:deploy</span> <span class="hljs-keyword">do</span>
    upload_to_play_store(<span class="hljs-symbol">track:</span> <span class="hljs-string">'production'</span>, <span class="hljs-symbol">aab:</span> <span class="hljs-string">'../build/app/outputs/bundle/release/app-release.aab'</span>)
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
</code></pre><p>Now you've created two lanes one that will push the build to the internal testing <code>beta</code> track and another one <code>deploy</code> to production.</p>
<p>Run the <code>fastlane supply init</code> to fetch information from the play console about the app (like screenshots, app descriptions, etc).</p>
<p>You can test if your setup works by running:</p>
<pre><code><span class="hljs-attribute">fastlane</span> beta
</code></pre><h4 id="heading-setting-up-github-action">Setting up Github Action</h4>
<p>Now that we know our configuration works locally, let's set up a GitHub action that will create a beta release every time we push our app into the development branch.</p>
<p>First, we need to add some repository secrets by going to <strong>Repository Settings</strong> &gt; <strong>Secrets</strong> &gt;&gt; <strong>Actions</strong> and add the following variables:</p>
<ul>
<li><code>PLAY_STORE_UPLOAD_KEY</code> this is the base64 encoded Keystore file (.jks)</li>
<li><code>KEYSTORE_KEY_ALIAS</code> this is the upload Keystore key alias</li>
<li><code>KEYSTORE_KEY_PASSWORD</code> this is our upload key password</li>
<li><code>KEYSTORE_STORE_PASSWORD</code> this is our store password</li>
<li><code>PLAY_STORE_CONFIG_JSON</code> this is the google service account's config file</li>
</ul>
<blockquote>
<p>use the <code>cat keystore.jks | base64 | pbcopy</code> to copy the base64 version of jks file to the clipboard.</p>
</blockquote>
<pre><code><span class="hljs-attribute">name</span>: Deploy Beta build to Play Store

<span class="sql">on:
  push:
    branches:
      - development

<span class="hljs-comment"># Declare default permissions as read only.</span>
permissions: read-all

jobs:
  fastlane-deploy:
    runs-on: ubuntu-20.04
    steps:
      <span class="hljs-comment"># Set up Flutter.</span>
      - name: Checkout App Repo
        uses: actions/checkout@v3
        <span class="hljs-keyword">with</span>:
          <span class="hljs-keyword">ref</span>: <span class="hljs-string">'development'</span>

      - <span class="hljs-keyword">name</span>: Setup <span class="hljs-keyword">Java</span>
        uses: actions/setup-<span class="hljs-keyword">java</span>@v1
        <span class="hljs-keyword">with</span>:
          <span class="hljs-keyword">java</span>-<span class="hljs-keyword">version</span>: <span class="hljs-string">'12.x'</span>

      - <span class="hljs-keyword">name</span>: Configure Keystore
        run: |
          echo <span class="hljs-string">"$PLAY_STORE_UPLOAD_KEY"</span> | base64 <span class="hljs-comment">--decode &gt; /home/runner/work/repo-name/repo-name/upload-keystore.jks</span>
          echo <span class="hljs-string">"storeFile=/home/runner/work/repo-name/repo-name/upload-keystore.jks"</span> &gt;&gt; key.properties
          echo <span class="hljs-string">"keyAlias=$KEYSTORE_KEY_ALIAS"</span> &gt;&gt; key.properties
          echo <span class="hljs-string">"storePassword=$KEYSTORE_STORE_PASSWORD"</span> &gt;&gt; key.properties
          echo <span class="hljs-string">"keyPassword=$KEYSTORE_KEY_PASSWORD"</span> &gt;&gt; key.properties
          echo <span class="hljs-string">"$KEYSTORE_KEY_ALIAS"</span>
          echo <span class="hljs-string">"$PLAY_STORE_CONFIG_JSON"</span> &gt; /home/runner/<span class="hljs-keyword">work</span>/repo-<span class="hljs-keyword">name</span>/repo-<span class="hljs-keyword">name</span>/service_key.json
          echo <span class="hljs-string">"json_key_file(\"</span>/home/runner/<span class="hljs-keyword">work</span>/repo-<span class="hljs-keyword">name</span>/repo-<span class="hljs-keyword">name</span>/service_key.json\<span class="hljs-string">")"</span> &gt; /home/runner/<span class="hljs-keyword">work</span>/repo-<span class="hljs-keyword">name</span>/repo-<span class="hljs-keyword">name</span>/android/fastlane/Appfile
          echo <span class="hljs-string">"package_name(\"</span><span class="hljs-keyword">package</span>-<span class="hljs-keyword">name</span>\<span class="hljs-string">")"</span> &gt;&gt; /home/runner/<span class="hljs-keyword">work</span>/repo-<span class="hljs-keyword">name</span>/repo-<span class="hljs-keyword">name</span>/android/fastlane/Appfile
          cat /home/runner/<span class="hljs-keyword">work</span>/repo-<span class="hljs-keyword">name</span>/repo-<span class="hljs-keyword">name</span>/android/fastlane/Appfile
        env:
          PLAY_STORE_UPLOAD_KEY: ${{ secrets.PLAY_STORE_UPLOAD_KEY }}
          KEYSTORE_KEY_ALIAS: ${{ secrets.KEYSTORE_KEY_ALIAS }}
          KEYSTORE_KEY_PASSWORD: ${{ secrets.KEYSTORE_KEY_PASSWORD }}
          KEYSTORE_STORE_PASSWORD: ${{ secrets.KEYSTORE_STORE_PASSWORD }}
          PLAY_STORE_CONFIG_JSON: ${{ secrets.PLAY_STORE_CONFIG_JSON }}
        working-<span class="hljs-keyword">directory</span>: android

      - <span class="hljs-keyword">name</span>: <span class="hljs-keyword">Install</span> Flutter &amp; <span class="hljs-keyword">Build</span> App Bundle
        uses: subosito/flutter-<span class="hljs-keyword">action</span>@v2
        <span class="hljs-keyword">with</span>:
          flutter-<span class="hljs-keyword">version</span>: <span class="hljs-string">'3.x'</span>
          channel: <span class="hljs-string">'stable'</span>
      - run: flutter <span class="hljs-comment">--version &amp;&amp; flutter pub get &amp;&amp; flutter build appbundle --release</span>

      - <span class="hljs-keyword">name</span>: Setup Ruby
        uses: ruby/setup-ruby@v1
        <span class="hljs-keyword">with</span>:
          ruby-<span class="hljs-keyword">version</span>: <span class="hljs-string">'2.7.2'</span>

      - <span class="hljs-keyword">name</span>: Fastlane <span class="hljs-keyword">Action</span>
        uses: maierj/fastlane-<span class="hljs-keyword">action</span>@v2<span class="hljs-number">.2</span><span class="hljs-number">.1</span>
        <span class="hljs-keyword">with</span>:
          lane: <span class="hljs-string">"beta"</span>
          subdirectory: <span class="hljs-string">"android"</span></span>
</code></pre><blockquote>
<p>make sure to replace the <code>repo-name</code> and <code>package-name</code> in the <code>Configure Keystore</code> step.</p>
</blockquote>
<p>That's all, everytime you push to the development branch a new draft release will be automatically created in the play console.</p>
]]></content:encoded></item><item><title><![CDATA[Array & Slice in GoLang]]></title><description><![CDATA[Let's say we are working on a go app and we have to store the data of 2 users, so far we know about variables and how they can be used for data, let's say we create two variables and store the name of our 2 users. But then we receive a request to add...]]></description><link>https://blog.vivekkaushik.com/array-and-slice-in-golang</link><guid isPermaLink="true">https://blog.vivekkaushik.com/array-and-slice-in-golang</guid><category><![CDATA[golang]]></category><category><![CDATA[Go Language]]></category><category><![CDATA[array]]></category><category><![CDATA[slice]]></category><dc:creator><![CDATA[Vivek Kaushik]]></dc:creator><pubDate>Sun, 10 Jul 2022 16:24:23 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1657468401993/wDie6C8y8.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Let's say we are working on a go app and we have to store the data of 2 users, so far we know about variables and how they can be used for data, let's say we create two variables and store the name of our 2 users. But then we receive a request to add another user well, we can create another variable for our new user. But you can see this is becoming a problem. Our application is not scalable and the code will also start to become a mess after a certain number of users. How do we get around this problem?</p>
<p>Meet Arrays and Slices.</p>
<h3 id="heading-array">Array</h3>
<p>An array is also like a variable it has an identifier and a data type. But unlike a variable we can store data many different predefined numbers of elements. </p>
<p>Let's say for 100 users, we create an array:</p>
<pre><code class="lang-go"><span class="hljs-keyword">var</span> users = [<span class="hljs-number">100</span>]<span class="hljs-keyword">string</span>{}   <span class="hljs-comment">// an empty string array of 100 elements</span>
</code></pre>
<p>When creating an array we need to know its size beforehand (how many elements it can hold).</p>
<p>First, we're defining a variable <code>users</code> which holds the memory address for the array, after the <code>=</code> sign we're defining the size of the array and then the type of data this array will hold, and finally the <code>{}</code> is the initial value of the array, which is currently empty. </p>
<p>You can also add initial values by providing them in the <code>{}</code> separated by a comma.</p>
<pre><code class="lang-go"><span class="hljs-keyword">var</span> users = [<span class="hljs-number">100</span>]<span class="hljs-keyword">string</span>{<span class="hljs-string">"Vivek"</span>, <span class="hljs-string">"Harsh"</span>, <span class="hljs-string">"Kartik"</span>}   <span class="hljs-comment">// an string array with 3 initial value</span>
</code></pre>
<p>How about accessing and storing the data?</p>
<pre><code class="lang-go"><span class="hljs-keyword">var</span> users = [<span class="hljs-number">50</span>]<span class="hljs-keyword">string</span>{<span class="hljs-string">"Vivek"</span>, <span class="hljs-string">"Harsh"</span>, <span class="hljs-string">"Kartik"</span>}

users[<span class="hljs-number">3</span>] = <span class="hljs-string">"Sachin"</span> <span class="hljs-comment">// storing the user at 3rd index</span>

fmt.Println(users[<span class="hljs-number">0</span>]) <span class="hljs-comment">// this will print "Vivek"</span>
fmt.Println(users[<span class="hljs-number">1</span>]) <span class="hljs-comment">// this will print "Harsh"</span>

users[<span class="hljs-number">0</span>] = <span class="hljs-string">"Vivek Kaushik"</span> <span class="hljs-comment">// Reassign the value at index 0</span>
fmt.Println(users[<span class="hljs-number">1</span>]) <span class="hljs-comment">// Will print "Vivek Kaushik"</span>

fmt.Printf(<span class="hljs-string">"length of the array: %v\n"</span>, <span class="hljs-built_in">len</span>(users))  <span class="hljs-comment">// Will print "length of the array: 100"</span>
</code></pre>
<p>Arrays have something called as Index. Think of the array as a box with compartments, and each compartment has a number assigned to it (index). The number starts from 0 to the length of the array minus 1. So for our previous user example, we will have index from 0 to 99. We can use these indexes to access, assign or reassign values.</p>
<p>If we try to access a value from an index that is out of range, from our previous example <code>fmt.Println(users[100])</code> this will give us an <code>Index out of bound</code> error as we only had index up to 99.</p>
<blockquote>
<p>Note: 
<code>len()</code> is another function, that gives us the size of the array. </p>
</blockquote>
<h3 id="heading-slices">Slices</h3>
<p>Arrays are great but they are not always useful, as most of the time we do not know how many elements we will be adding. This is where <code>Slices</code> comes in.</p>
<p>For slices we don't have to define size, they are dynamic and can grow and shrink as needed.</p>
<pre><code class="lang-go"><span class="hljs-keyword">var</span> users = []<span class="hljs-keyword">string</span>{} <span class="hljs-comment">// empty slice of 0 length</span>

fmt.Printf(<span class="hljs-string">"length of the slice: %v\n"</span>, <span class="hljs-built_in">len</span>(users))  <span class="hljs-comment">// Will print "length of the slice: 0"</span>

users = <span class="hljs-built_in">append</span>(users, <span class="hljs-string">"Vivek"</span>)

fmt.Printf(<span class="hljs-string">"length of the slice: %v\n"</span>, <span class="hljs-built_in">len</span>(users))  <span class="hljs-comment">// Will print "length of the slice: 1"</span>

fmt.Println(users[<span class="hljs-number">0</span>]) <span class="hljs-comment">// this will print "Vivek"</span>
</code></pre>
<p>First, we initialize an empty slice which is very similar to an array except we don't specify the size of it. Next we print the size of our newly defined slice using the <code>len</code> function which gives us zero. Then we use another function called <code>append</code> which takes our slice and a value and appends it to the end of slice. After using the <code>len</code> function again we see that the size is now 1. Finally we print the value at index  0.</p>
]]></content:encoded></item><item><title><![CDATA[Variables And Data Types In Go]]></title><description><![CDATA[Variables are containers that hold some value. There are two types of variables in Go, one whose value can be changed and the other whose can not we call them constants.
We can define a variable using the var keyword followed by the variable name and...]]></description><link>https://blog.vivekkaushik.com/variables-and-data-types-in-go</link><guid isPermaLink="true">https://blog.vivekkaushik.com/variables-and-data-types-in-go</guid><category><![CDATA[Go Language]]></category><category><![CDATA[golang]]></category><dc:creator><![CDATA[Vivek Kaushik]]></dc:creator><pubDate>Tue, 05 Jul 2022 16:21:56 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1657037081822/m162TPfoN.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Variables are containers that hold some value. There are two types of variables in Go, one whose value can be changed and the other whose can not we call them <code>constants</code>.</p>
<p>We can define a variable using the <code>var</code> keyword followed by the variable name and then its data type (what type value it is expected to store). Here are some examples:</p>
<pre><code class="lang-go"><span class="hljs-keyword">var</span> name <span class="hljs-keyword">string</span> = <span class="hljs-string">"Go Language"</span>
<span class="hljs-keyword">var</span> number <span class="hljs-keyword">int</span> = <span class="hljs-number">23</span>
<span class="hljs-keyword">var</span> isTrue <span class="hljs-keyword">bool</span> = <span class="hljs-literal">true</span>

fmt.Println(name)
fmt.Println(<span class="hljs-keyword">int</span>)
fmt.Println(isTrue)
</code></pre>
<blockquote>
<p>Using just <code>var name string</code> and not setting it to any value, it is called variable definition, we are defining what a variable's name is and what kind of data it can store and if it can be reassigned (set to a new value) or not.</p>
<p>When we assign a value to a variable for the first time it is called variable initialization.</p>
</blockquote>
<p>Constants are defined the same way as variables instead of using var we use <code>const</code>.</p>
<pre><code class="lang-go"><span class="hljs-keyword">const</span> name = <span class="hljs-string">"Vivek"</span>
name = <span class="hljs-string">"Harsh"</span> <span class="hljs-comment">// Error: cannot assign to name</span>
</code></pre>
<h3 id="heading-data-types">Data Types</h3>
<p>Go is statically typed, meaning, we need to tell the compiler about what type of data we are expecting. </p>
<p>We have multiple data types for different purposes. Here are some of them:</p>
<ul>
<li>String</li>
<li>Integer</li>
<li>Float</li>
<li>Boolean</li>
<li>Array</li>
<li>Map</li>
</ul>
<h4 id="heading-string">String</h4>
<p>For textual data, we use the string type. We've been using the string data type from the start.</p>
<p>Anything within a double quote is considered a string. Here's an example:</p>
<pre><code class="lang-go"><span class="hljs-keyword">var</span> text <span class="hljs-keyword">string</span> = <span class="hljs-string">"Hello"</span>
</code></pre>
<h4 id="heading-integers">Integers</h4>
<p>Integer represents whole numbers positive or negative, like 5, -5, 0.</p>
<pre><code class="lang-go"><span class="hljs-keyword">var</span> positiveInt <span class="hljs-keyword">int</span> = <span class="hljs-number">5</span>
<span class="hljs-keyword">var</span> negativeInt <span class="hljs-keyword">int</span> = <span class="hljs-number">-5</span>
<span class="hljs-keyword">var</span> zero <span class="hljs-keyword">int</span> = <span class="hljs-number">0</span>
</code></pre>
<p>Signed integers, declared with int can store both positive and negative whole numbers. But there is a limit to how big of a number they can store.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Type</strong></td><td><strong>Size</strong></td><td><strong>Range</strong></td></tr>
</thead>
<tbody>
<tr>
<td>int</td><td>32 or 64 bits depending on 32 or 64 bit system</td><td>See the respective column</td></tr>
<tr>
<td>int8</td><td>8 bits</td><td>-128 to 127</td></tr>
<tr>
<td>int16</td><td>16 bits</td><td>-32768 to 32767</td></tr>
<tr>
<td>int32</td><td>32 bits</td><td>-2147483648 to 2147483647</td></tr>
<tr>
<td>int64</td><td>64 bits</td><td>-9223372036854775808 to 9223372036854775807</td></tr>
</tbody>
</table>
</div><blockquote>
<p><strong>Note:</strong>
There are also <code>Unsigned</code> types for integers.</p>
</blockquote>
<h4 id="heading-unsigned-integers">Unsigned Integers</h4>
<p>Unsigned integers are declared with the <code>uint</code> keyword, these can only store positive whole numbers (yes this includes zero).</p>
<p>Here are the different variations of <code>uint</code> and their limitations:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Type</strong></td><td><strong>Size</strong></td><td><strong>Range</strong></td></tr>
</thead>
<tbody>
<tr>
<td>uint</td><td>32 or 64 bits depending on 32 or 64 bit system</td><td>See the respective column</td></tr>
<tr>
<td>uint8</td><td>8 bits</td><td>0 to 255</td></tr>
<tr>
<td>uint16</td><td>16 bits</td><td>0 to 65535</td></tr>
<tr>
<td>uint32</td><td>32 bits</td><td>0 to 4294967295</td></tr>
<tr>
<td>uint64</td><td>64 bits</td><td>0 to 18446744073709551615</td></tr>
</tbody>
</table>
</div><h4 id="heading-float">Float</h4>
<p>The float data types can store positive or negative decimal numbers:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Type</strong></td><td><strong>Size</strong></td><td><strong>Range</strong></td></tr>
</thead>
<tbody>
<tr>
<td>float32</td><td>32 bits</td><td>-3.4e+38 to 3.4e+38.</td></tr>
<tr>
<td>float64</td><td>64 bits</td><td>-1.7e+308 to +1.7e+308.</td></tr>
</tbody>
</table>
</div><p>Here is an example of how we can use floats:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main
<span class="hljs-keyword">import</span> (<span class="hljs-string">"fmt"</span>)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">var</span> x <span class="hljs-keyword">float64</span> = <span class="hljs-number">234.43</span>
    <span class="hljs-keyword">var</span> y <span class="hljs-keyword">float32</span> = <span class="hljs-number">3.4e+38</span>
    fmt.Printf(<span class="hljs-string">"Type: %T, value: %v\n"</span>, x, x)
    fmt.Printf(<span class="hljs-string">"Type: %T, value: %v"</span>, y, y)
}
</code></pre>
<p><code>fmt.Printf("text")</code> is another method from the "fmt" package it is used to format an insert text at some predefined placeholders. You can learn more about fmt <a target="_blank" href="https://pkg.go.dev/fmt">here</a>.</p>
<h4 id="heading-boolean">Boolean</h4>
<p>A variable with <code>bool</code> data type can store either <code>true</code> or <code>false</code>. the default value for a bool variable is false.</p>
<pre><code class="lang-go"><span class="hljs-keyword">var</span> isTrue <span class="hljs-keyword">bool</span> = <span class="hljs-literal">true</span>
<span class="hljs-keyword">var</span> isFalse <span class="hljs-keyword">bool</span> = <span class="hljs-literal">false</span>
<span class="hljs-keyword">var</span> defaultValue <span class="hljs-keyword">bool</span>

fmt.Println(<span class="hljs-string">"isTrue:"</span>, isTrue)
fmt.Println(<span class="hljs-string">"isFalse:"</span>, isFalse)
fmt.Println(<span class="hljs-string">"defaultValue:"</span>, defaultValue)
</code></pre>
<blockquote>
<p><strong>Note: </strong>
We'll talk about the arrays and maps separately.</p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Getting Started With Go]]></title><description><![CDATA[In this post, I'll walk you through setting up your go project. As a prerequisite you must have go installed in your system, you can get it for your system from https://go.dev/dl/
Now that that's out of the way, go ahead and create a directory called...]]></description><link>https://blog.vivekkaushik.com/getting-started-with-go</link><guid isPermaLink="true">https://blog.vivekkaushik.com/getting-started-with-go</guid><category><![CDATA[learning]]></category><category><![CDATA[golang]]></category><category><![CDATA[Go Language]]></category><dc:creator><![CDATA[Vivek Kaushik]]></dc:creator><pubDate>Mon, 04 Jul 2022 08:58:24 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1656870470158/TILbiKESj.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In this post, I'll walk you through setting up your go project. As a prerequisite you must have go installed in your system, you can get it for your system from <a target="_blank" href="https://go.dev/dl/">https://go.dev/dl/</a></p>
<p>Now that that's out of the way, go ahead and create a directory called <code>my_project</code>, Inside the directory you need a <code>go.mod</code> at the root of the project, this file that keeps track of the modules that provide those package (this of it as package.json file if you're coming from Java Script). You can create the go.mod file using <code>go mod init module_name</code> command, you can use your own module name, typical practice is to use your version control repo path for ex <code>github.com/iamvivekkaushik/my_project</code>. That is if you want to publish your module for others, the module path must be a location from which Go tools can download your module.</p>
<p>Now create a <code>main.go</code> file that will contain our go code and paste the following code.</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> <span class="hljs-string">"fmt"</span>

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    fmt.Println(<span class="hljs-string">"Hello, World!"</span>)
}
</code></pre>
<p>To run your program use:</p>
<pre><code>$ go run main.app
Hello World<span class="hljs-operator">!</span>
</code></pre><p>When developing an executable program, we use the package <code>main</code>. The package “main” tells the Go compiler that the package should compile as an executable program instead of a shared library. </p>
<p>We will separate our source code into separate packages, this is to enable re-usablity of the code. The naming convention for Go package is to use the name of the system directory where we're putting the source file, the package will be same for each file within a directory.</p>
<p>After defining the package we use the import statement to import a package called <code>fmt</code>, probably short for formatter. The package <code>fmt</code> comes from the Go standard library. When we import packages, the Go compiler will look on the locations specified by the environment variable <code>GOROOT</code> and <code>GOPATH</code>.</p>
<p>Then we define a <code>main</code> function using the func keyword and use the "fmt" to print the "Hello World!" text.</p>
<p>If you're coming from JS you might be wondering I never called the main function (we will learn about functions in the coming posts) how is it running. So the main function is actually automatically called when you run the program, this is called the entrypoint of your project. You must have a main function defined in your main package.</p>
<hr />
<p>Here are some commands to help you get started.</p>
<p>To add  missing module requirements or remove unneeded requirements, use:</p>
<pre><code><span class="hljs-keyword">go</span> mod tidy
</code></pre><p>To add a third party package:</p>
<pre><code>go get <span class="hljs-operator">&lt;</span>module_url<span class="hljs-operator">&gt;</span>
</code></pre>]]></content:encoded></item><item><title><![CDATA[Setting up PostgreSQL and configuring incoming connections]]></title><description><![CDATA[In this article, I'll give you a step-by-step guide on how to install the PostgreSQL database on Ubuntu 20.04.
First of all, you'll need to update your sources using
user@ubuntu:~$ sudo apt update
Once that's done go ahead and use the apt package man...]]></description><link>https://blog.vivekkaushik.com/setting-up-postgresql-and-configuring-incoming-connections</link><guid isPermaLink="true">https://blog.vivekkaushik.com/setting-up-postgresql-and-configuring-incoming-connections</guid><category><![CDATA[postgres]]></category><category><![CDATA[PostgreSQL]]></category><category><![CDATA[Ubuntu]]></category><dc:creator><![CDATA[Vivek Kaushik]]></dc:creator><pubDate>Sun, 06 Mar 2022 11:12:07 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1646559105332/Kyf7cKBiO.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In this article, I'll give you a step-by-step guide on how to install the PostgreSQL database on Ubuntu 20.04.</p>
<p>First of all, you'll need to update your sources using</p>
<pre><code>user@ubuntu<span class="hljs-symbol">:~</span>$ sudo apt update
</code></pre><p>Once that's done go ahead and use the apt package manager to install postgrsql:</p>
<pre><code>user@ubuntu<span class="hljs-symbol">:~</span>$ sudo apt install postgresql postgresql-contrib
</code></pre><p>The above command will install the PostgreSQL database on your system. By default Postgres automatically creates a user with the name of <code>postgres</code>, this user has full superuser access.</p>
<p>To access the <code>psql</code> interface (psql is a command-line based front-end for Postgres 
you can use psql to interface with Postgres) use the command below:</p>
<pre><code>user@ubuntu<span class="hljs-symbol">:~</span>$ sudo -u postgres psql
</code></pre><p>or you can use the postgres user that was created by Postgres upon installation and use the psql command from there.</p>
<pre><code>user@ubuntu<span class="hljs-symbol">:~</span>$ su postgres
postgres@ubuntu<span class="hljs-symbol">:~</span>$ psql
</code></pre><p>Notice how the first command <code>su postgres</code> changed the user to postgres. Once you run the <code>psql</code> command an interface similar to the one below will show up, this is where you can run SQL and Postgres specific queries.</p>
<pre><code><span class="hljs-attribute">psql</span> (<span class="hljs-number">12</span>.<span class="hljs-number">9</span> (Ubuntu <span class="hljs-number">12</span>.<span class="hljs-number">9</span>-<span class="hljs-number">0</span>ubuntu<span class="hljs-number">0.20.04.1</span>))
<span class="hljs-attribute">Type</span> <span class="hljs-string">"help"</span> for help.

<span class="hljs-attribute">postgres</span>=#
</code></pre><h2 id="heading-creating-a-new-database">Creating a new database</h2>
<p>You can use the command shown below to create a user first:</p>
<pre><code>postgres=# <span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">USER</span> <span class="hljs-string">'&lt;username&gt;'</span> <span class="hljs-keyword">WITH</span> <span class="hljs-keyword">ENCRYPTED</span> <span class="hljs-keyword">PASSWORD</span> <span class="hljs-string">'&lt;new-password&gt;'</span>;
</code></pre><p>The above command will create a new user with a username and a password, make sure to change those to whatever you prefer (don't use <code>&lt;&gt;</code>).</p>
<p>Once the user is created, we'll need to create a database.</p>
<pre><code>postgres=# <span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">DATABASE</span> <span class="hljs-string">'&lt;db-name&gt;'</span>;
</code></pre><p>The above command will create a new database (Make sure to change the <code>&lt;db-name&gt;</code>). Now we need to grant access to this database to the user we created in the previous step.</p>
<pre><code>postgres=# <span class="hljs-keyword">GRANT</span> <span class="hljs-keyword">ALL</span> <span class="hljs-keyword">PRIVILEGES</span> <span class="hljs-keyword">ON</span> <span class="hljs-keyword">DATABASE</span> <span class="hljs-string">'&lt;db-name&gt;'</span> <span class="hljs-keyword">TO</span> <span class="hljs-string">'&lt;username&gt;'</span>;
</code></pre><p>Now that our database is created and a user has access to that database the basic step is done. But if you want your database to be accessible everywhere or only to a specific IP, keep reading.</p>
<h2 id="heading-modifying-connections-to-the-database">Modifying connections to the database</h2>
<p>Now that you have your database up and running you might want to modify who can have access to it. You can either allow everyone (which is not a good idea) or a specific set of IPs (using CIDR).</p>
<p>To achieve this we will need to edit the Postgres config which can be found in <code>/etc/postgresql/12/main/</code> path may vary based on Postgres version.</p>
<pre><code><span class="hljs-keyword">user</span>@ubuntu:~$ cd /etc/postgresql/<span class="hljs-number">12</span>/main/
<span class="hljs-keyword">user</span>@ubuntu:~$ ls
conf.d  environment  pg_ctl.conf  pg_hba.conf  pg_ident.conf  postgresql.conf <span class="hljs-keyword">start</span>.conf
</code></pre><p>As you can see there are many available files here, but the ones we are interested in are <code>postgresql.conf</code> and <code>pg_hba.conf</code>. Use the nano text editor to edit the postgresql.conf file.</p>
<pre><code>user@ubuntu<span class="hljs-symbol">:~</span>$ sudo nano postgresql.conf
</code></pre><p>You'll have to scroll down a bit until you see a section called <code>CONNECTIONS AND AUTHENTICATION</code> it will look something like this:</p>
<pre><code><span class="hljs-comment">#------------------------------------------------------------------------------</span>
<span class="hljs-comment"># CONNECTIONS AND AUTHENTICATION</span>
<span class="hljs-comment">#------------------------------------------------------------------------------</span>
<span class="hljs-comment"># - Connection Settings -</span>
<span class="hljs-attr">listen_addresses</span> = <span class="hljs-string">'localhost,192.168.0.3'</span>         <span class="hljs-comment"># what IP address(es) to listen on;</span>
</code></pre><p>You'll need to assign the list of IPs that you want to have access to Postgres to  <code>listen_addresses</code>. Alternatively, you can set listen_addresses equal to <code>'*'</code> which will mean that anyone on the internet can have access to our database (Bad idea).</p>
<p>Now save this file by pressing <code>ctrl+x</code> and tying <code>Y</code> and accepting the provided name.</p>
<p>Finally, we'll need to edit the <code>pg_hba.conf</code> file. This file controls the client authentication. We'll add our database and user and what IP can authenticate with them. Previously what we did was, we specified who can make a connection to the database.</p>
<p>Open the <code>pg_hba.conf</code> file using nano:</p>
<pre><code><span class="hljs-keyword">user</span>@ubuntu:~$ sudo nano pg_hba.conf
</code></pre><p>At the bottom of the file specify your user and database and what IPs are allowed to authenticate.</p>
<pre><code><span class="hljs-attribute">host</span>    username     db-name     <span class="hljs-number">192.168.0.0</span>/<span class="hljs-number">24</span>        md<span class="hljs-number">5</span>
</code></pre><p>Make sure to change the username and db-name with the one you created and the IP address to the IP of your computer that you'll use to authenticate.</p>
<p>Finally, save the file using <code>ctrl+x</code> then <code>Y</code> and accept the provided file name.</p>
<p>You can use telnet command  to check if you can make a connection to the db:</p>
<pre><code><span class="hljs-string">user@ubuntu:~$</span> <span class="hljs-string">telnet</span> <span class="hljs-number">192.168</span><span class="hljs-number">.0</span><span class="hljs-number">.2</span> <span class="hljs-number">5241</span>
</code></pre><p>Change the IP with the Postgres server IP. <code>5241</code> is the default port for postgres.</p>
<p>And that's it. Thanks for reading.</p>
]]></content:encoded></item><item><title><![CDATA[Setting Up Verified Commits On Github]]></title><description><![CDATA[We've all seen that verified badge on GitHub commits when we edit something straight from Github Web UI. Surely you've wondered why it doesn't show up when you push something from the terminal, even though you successfully authenticated yourself with...]]></description><link>https://blog.vivekkaushik.com/setting-up-verified-commits-on-github</link><guid isPermaLink="true">https://blog.vivekkaushik.com/setting-up-verified-commits-on-github</guid><category><![CDATA[GitHub]]></category><dc:creator><![CDATA[Vivek Kaushik]]></dc:creator><pubDate>Sun, 28 Mar 2021 05:38:39 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1616909744106/TrEUiRr4I.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>We've all seen that verified badge on GitHub commits when we edit something straight from Github Web UI. Surely you've wondered why it doesn't show up when you push something from the terminal, even though you successfully authenticated yourself with the correct username and password. I mean it should show up because it is you who are making those changes, Right?</p>
<p>Well turns you need to set up GPG keys for your machines in order to have that luxury. In fact, there is an official guide from Github telling users how to set up these keys. Honestly, that's the one I used when I was trying to figure out how this all works. If you're interested you can check the official guide  <a target="_blank" href="https://docs.github.com/en/github/authenticating-to-github/managing-commit-signature-verification">here</a>.</p>
<h3 id="heading-creating-a-new-gpg-key">Creating a new GPG key</h3>
<p>First, you need to install the <code>GnuPG Binary</code> for your operating system. You can download the same from  <a target="_blank" href="https://www.gnupg.org/download/#binary">here</a>.</p>
<p>Now that you've installed the binary, go ahead and open up a terminal.</p>
<p>Use the command below to generate the GPG key</p>
<pre><code class="lang-bash">gpg --full-generate-key
</code></pre>
<p>First Enter your Full Name.</p>
<p>If prompted, specify the kind of key you want, or press Enter to accept the default RSA and RSA.</p>
<p>Next if asked you need to set the maximum recommended key size to <code>4096</code>.</p>
<p>Next, enter the expiry date for key expiry.</p>
<p>Enter your user ID information.</p>
<blockquote>
<p>Make sure that you enter the verified email address for your GitHub account when entering the email. To keep your email address private, use your GitHub-provided no-reply email address. For more information, see "<a target="_blank" href="https://docs.github.com/en/articles/verifying-your-email-address">Verifying your email address</a>" and "<a target="_blank" href="https://docs.github.com/en/github/setting-up-and-managing-your-github-user-account/setting-your-commit-email-address">Setting your commit email address</a>".</p>
</blockquote>
<p>Last, type a secure passphrase.</p>
<p>Use the <code>gpg --list-secret-keys --keyid-format LONG</code> command to list GPG keys for which you have both a public and private key. A private key is required for signing commits or tags.</p>
<p>From the list of GPG keys, copy the GPG key ID you'd like to use. In this example, the GPG key ID is 3AA5C34371567BD2:</p>
<pre><code class="lang-bash">$ gpg --list-secret-keys --keyid-format LONG
/Users/hubot/.gnupg/secring.gpg
------------------------------------
sec   4096R/3AA5C34371567BD2 2016-03-10 [expires: 2017-03-10]
      57D3288AAF1EC3AA5C34371567A243S0946AA2
uid                 Vivek Kaushik &lt;your_email@gmail.com&gt; 
ssb   4096R/42B317FD4BA89E7A 2016-03-10
</code></pre>
<p>Use the command below, substituting in the GPG key ID you'd like to use. In this example, the GPG key ID is 3AA5C34371567BD2:</p>
<pre><code class="lang-bash">$ gpg --armor --<span class="hljs-built_in">export</span> 3AA5C34371567BD2
<span class="hljs-comment"># Prints the GPG key ID, in ASCII armor format</span>
</code></pre>
<p>Copy your GPG key, beginning with <code>-----BEGIN PGP PUBLIC KEY BLOCK-----</code> and ending with <code>-----END PGP PUBLIC KEY BLOCK-----</code>.</p>
<h3 id="heading-adding-a-new-gpg-key-to-your-github-account">Adding a new GPG key to your GitHub account</h3>
<p>In the upper-right corner of any page, click your profile photo, then click <code>Settings</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1616908481511/Q7biaNWta.png" alt="Screenshot 2021-03-28 at 10.44.27 AM.png" /></p>
<p>In the user settings sidebar, click <code>SSH and GPG keys</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1616908590303/pIfZsh8xP.png" alt="Screenshot 2021-03-28 at 10.45.20 AM.png" /></p>
<p>Click <code>New GPG key</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1616908664602/wLHaIbdf_.png" alt="Screenshot 2021-03-28 at 10.46.52 AM.png" /></p>
<p>In the "Key" field, paste the GPG key you copied when you generated your GPG key and then click on <code>Add GPG key</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1616908736324/Uui_Wcyql.png" alt="Screenshot 2021-03-28 at 10.48.36 AM.png" /></p>
<h3 id="heading-telling-git-about-your-signing-key">Telling Git about your signing key</h3>
<p>Use the <code>gpg --list-secret-keys --keyid-format LONG</code> command to list GPG keys for which you have both a public and private key. A private key is required for signing commits or tags.</p>
<pre><code class="lang-bash">$ gpg --list-secret-keys --keyid-format LONG
/Users/hubot/.gnupg/secring.gpg
------------------------------------
sec   4096R/3AA5C34371567BD2 2016-03-10 [expires: 2017-03-10]
      57D3288AAF1EC3AA5C34371567AB0020946AA2
uid                 Vivek Kaushik &lt;your_email@gmail.com&gt; 
ssb   4096R/42B317FD4BA89E7A 2016-03-10
</code></pre>
<p>To set your GPG signing key in Git, paste the text below, substituting in the GPG key Hash you'd like to use. In this example, the GPG key Hash is 57D3288AAF1EC3AA5C34371567A243S0946AA2:</p>
<pre><code class="lang-bash">git config --global user.signingkey 57D3288AAF1EC3AA5C34371567A243S0946AA2
</code></pre>
<p>To configure your Git client to sign commits by default, in Git versions 2.0.0 and above, run </p>
<pre><code class="lang-bash">git config --global commit.gpgsign <span class="hljs-literal">true</span>
</code></pre>
<p>Now all you need to do is <code>commit</code> and <code>push</code> your code as you regularly do.</p>
]]></content:encoded></item><item><title><![CDATA[Disable Root And Password Login via SSH]]></title><description><![CDATA[I shouldn't need to tell you why login as root via SSH can be dangerous. If somebody (hacker/unwanted person) were to get into your server and they have root access, that can be a sign of trouble. You see with root access they can do virtually anythi...]]></description><link>https://blog.vivekkaushik.com/disable-root-and-password-login-via-ssh</link><guid isPermaLink="true">https://blog.vivekkaushik.com/disable-root-and-password-login-via-ssh</guid><category><![CDATA[ssh]]></category><category><![CDATA[Ubuntu]]></category><category><![CDATA[Security]]></category><dc:creator><![CDATA[Vivek Kaushik]]></dc:creator><pubDate>Fri, 26 Mar 2021 11:57:36 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1616910046721/_szzJtQCp.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I shouldn't need to tell you why login as root via SSH can be dangerous. If somebody (hacker/unwanted person) were to get into your server and they have root access, that can be a sign of trouble. You see with root access they can do virtually anything. So disabling root access can be a good move for your server security.</p>
<p>In order to disable root login, you must first ensure that there is a normal user account available for you to log in after root login is disabled.</p>
<p><strong>Creating a new user</strong></p>
<pre><code class="lang-bash">sudo adduser <span class="hljs-string">"username"</span>
</code></pre>
<p>Change the <code>username</code> with the desired username for your new user. Also, don't apply quotes. After executing the command you'll be presented with a form asking for the user's information, just enter whatever you want.</p>
<p>Now that the user is created, open the <code>/etc/ssh/sshd_config</code> inside your favourite text editor.</p>
<pre><code class="lang-bash">sudo nano /etc/ssh/sshd_config
</code></pre>
<p>In this file under <code>Authentication</code>, you'll find <code>PermitRootLogin</code> you need to change it's value to <code>no</code>.</p>
<pre><code><span class="hljs-attribute">PermitRootLogin</span> <span class="hljs-literal">no</span>
</code></pre><p>In case you also want to disable password-based authentication as well, you can also change the <code>PasswordAuthentication</code> to <code>no</code></p>
<pre><code><span class="hljs-attribute">PasswordAuthentication</span> <span class="hljs-literal">no</span>
</code></pre><p>But make sure you've set up ssh key-based authentication before disabling PasswordAuthentication. I'll link up the article here for setting up ssh-key-based authentication.</p>
<p>Now, you need to restart your <code>SSH</code> service.</p>
<pre><code class="lang-bash">sudo service ssh restart
</code></pre>
]]></content:encoded></item></channel></rss>