<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mohamad Ashraful Islam</title>
    <description>The latest articles on DEV Community by Mohamad Ashraful Islam (@ashraful).</description>
    <link>https://dev.to/ashraful</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F30490%2F9dc1ef64-0233-43b5-9127-79affd83cc43.jpg</url>
      <title>DEV Community: Mohamad Ashraful Islam</title>
      <link>https://dev.to/ashraful</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ashraful"/>
    <language>en</language>
    <item>
      <title>How I Built My Personal Blog Using the DEV.to API</title>
      <dc:creator>Mohamad Ashraful Islam</dc:creator>
      <pubDate>Tue, 21 Jul 2026 06:42:56 +0000</pubDate>
      <link>https://dev.to/ashraful/how-i-built-my-personal-blog-using-the-devto-api-142a</link>
      <guid>https://dev.to/ashraful/how-i-built-my-personal-blog-using-the-devto-api-142a</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Write once, publish everywhere.&lt;/strong&gt; That was the goal when I built my personal blog.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For years, I published articles directly on &lt;strong&gt;DEV.to&lt;/strong&gt; because of its amazing developer community and reach. But I also wanted a blog on my own domain where I had complete control over the design, branding, and user experience.&lt;/p&gt;

&lt;p&gt;Instead of maintaining two separate blogs, I decided to make &lt;strong&gt;DEV.to my CMS&lt;/strong&gt; and let my website fetch articles automatically using the DEV.to API.&lt;/p&gt;

&lt;p&gt;The result is my personal blog:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://blog.ashraful.dev/" rel="noopener noreferrer"&gt;https://blog.ashraful.dev/&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every time I publish a new article on DEV.to, it automatically appears on my website. No copy-pasting. No duplicated content. No extra work. :contentReference[oaicite:0]{index=0}&lt;/p&gt;




&lt;h1&gt;
  
  
  The Problem
&lt;/h1&gt;

&lt;p&gt;Initially, my workflow looked like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Write the article.&lt;/li&gt;
&lt;li&gt;Publish it on DEV.to.&lt;/li&gt;
&lt;li&gt;Copy the Markdown.&lt;/li&gt;
&lt;li&gt;Paste it into my website.&lt;/li&gt;
&lt;li&gt;Fix formatting.&lt;/li&gt;
&lt;li&gt;Publish again.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This wasn't difficult, but it was repetitive.&lt;/p&gt;

&lt;p&gt;Every blog post required maintaining two separate copies of the same content.&lt;/p&gt;

&lt;p&gt;Eventually, I asked myself:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Why am I manually copying content when DEV.to already provides an API?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That simple question completely changed my blogging workflow.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why I Chose DEV.to
&lt;/h1&gt;

&lt;p&gt;There are many blogging platforms available:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Medium&lt;/li&gt;
&lt;li&gt;Hashnode&lt;/li&gt;
&lt;li&gt;Ghost&lt;/li&gt;
&lt;li&gt;WordPress&lt;/li&gt;
&lt;li&gt;Notion&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I chose DEV.to because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It has a large developer community.&lt;/li&gt;
&lt;li&gt;Articles are easily discoverable.&lt;/li&gt;
&lt;li&gt;Markdown support is excellent.&lt;/li&gt;
&lt;li&gt;It provides a free public API.&lt;/li&gt;
&lt;li&gt;I don't need to manage another CMS. :contentReference[oaicite:1]{index=1}&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of building an admin panel for my blog, I simply use DEV.to as the content management system.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Architecture
&lt;/h1&gt;

&lt;p&gt;My setup is surprisingly simple.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;            Write Article
                  │
                  ▼
              DEV.to
                  │
            DEV.to API
                  │
                  ▼
          Personal Website
      (blog.ashraful.dev)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My website never stores blog posts locally.&lt;/p&gt;

&lt;p&gt;Instead, it fetches them directly from the DEV.to API whenever needed.&lt;/p&gt;

&lt;p&gt;This keeps everything synchronized automatically.&lt;/p&gt;




&lt;h1&gt;
  
  
  Fetching Articles
&lt;/h1&gt;

&lt;p&gt;DEV.to exposes a public endpoint for fetching a user's published articles.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET https://dev.to/api/articles?username=YOUR_USERNAME
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In JavaScript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://dev.to/api/articles?username=ashraful&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response includes useful metadata like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Title&lt;/li&gt;
&lt;li&gt;Description&lt;/li&gt;
&lt;li&gt;Cover image&lt;/li&gt;
&lt;li&gt;Published date&lt;/li&gt;
&lt;li&gt;Tags&lt;/li&gt;
&lt;li&gt;Reading time&lt;/li&gt;
&lt;li&gt;Slug&lt;/li&gt;
&lt;li&gt;URL&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No authentication is required for public articles. :contentReference[oaicite:2]{index=2}&lt;/p&gt;




&lt;h1&gt;
  
  
  Loading a Single Blog Post
&lt;/h1&gt;

&lt;p&gt;Listing articles is only half the job.&lt;/p&gt;

&lt;p&gt;When a visitor clicks on an article, the website fetches its full content.&lt;/p&gt;

&lt;p&gt;The API endpoint looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET https://dev.to/api/articles/{username}/{slug}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET https://dev.to/api/articles/ashraful/my-awesome-post
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response contains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Markdown&lt;/li&gt;
&lt;li&gt;Rendered HTML&lt;/li&gt;
&lt;li&gt;Cover image&lt;/li&gt;
&lt;li&gt;Metadata&lt;/li&gt;
&lt;li&gt;Reactions&lt;/li&gt;
&lt;li&gt;Comments count&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The most important field for me is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"body_markdown"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"# My Blog Post..."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's exactly what I render on my website. :contentReference[oaicite:3]{index=3}&lt;/p&gt;




&lt;h1&gt;
  
  
  Rendering Markdown
&lt;/h1&gt;

&lt;p&gt;Once I receive the Markdown, I render it like any normal Markdown document.&lt;/p&gt;

&lt;p&gt;This gives me complete control over:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Typography&lt;/li&gt;
&lt;li&gt;Code highlighting&lt;/li&gt;
&lt;li&gt;Tables&lt;/li&gt;
&lt;li&gt;Images&lt;/li&gt;
&lt;li&gt;Custom components&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because the original content is still Markdown, I don't lose any formatting.&lt;/p&gt;




&lt;h1&gt;
  
  
  My Publishing Workflow
&lt;/h1&gt;

&lt;p&gt;Publishing a new article has become incredibly simple.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Write Article
      │
      ▼
Publish to DEV.to
      │
      ▼
Done ✅
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it.&lt;/p&gt;

&lt;p&gt;There's no second publishing step.&lt;/p&gt;

&lt;p&gt;The website automatically displays the new article because it always reads from the API.&lt;/p&gt;




&lt;h1&gt;
  
  
  Benefits of This Approach
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Single Source of Truth
&lt;/h2&gt;

&lt;p&gt;There's only one copy of every article.&lt;/p&gt;

&lt;p&gt;I never have to wonder:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Which version is the latest?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;DEV.to is always the source.&lt;/p&gt;




&lt;h2&gt;
  
  
  No Duplicate Work
&lt;/h2&gt;

&lt;p&gt;Before:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Publish to DEV.to

↓

Copy Markdown

↓

Paste into Website

↓

Publish Again
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Publish to DEV.to

↓

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

&lt;/div&gt;



&lt;p&gt;One click instead of many.&lt;/p&gt;




&lt;h2&gt;
  
  
  Better SEO
&lt;/h2&gt;

&lt;p&gt;One concern when publishing content on multiple websites is duplicate content.&lt;/p&gt;

&lt;p&gt;The good news is that DEV.to supports &lt;strong&gt;canonical URLs&lt;/strong&gt;, allowing search engines to understand which version should be treated as the original. This helps avoid SEO issues when cross-posting articles. :contentReference[oaicite:4]{index=4}&lt;/p&gt;




&lt;h2&gt;
  
  
  Custom Design
&lt;/h2&gt;

&lt;p&gt;Although the content comes from DEV.to, my website has its own:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Layout&lt;/li&gt;
&lt;li&gt;Branding&lt;/li&gt;
&lt;li&gt;Navigation&lt;/li&gt;
&lt;li&gt;Theme&lt;/li&gt;
&lt;li&gt;Search&lt;/li&gt;
&lt;li&gt;Reading experience&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Visitors get a consistent experience while I continue using DEV.to as the backend.&lt;/p&gt;




&lt;h1&gt;
  
  
  Performance Considerations
&lt;/h1&gt;

&lt;p&gt;Calling an external API on every request isn't always ideal.&lt;/p&gt;

&lt;p&gt;To improve performance, you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cache API responses.&lt;/li&gt;
&lt;li&gt;Use Incremental Static Regeneration (ISR).&lt;/li&gt;
&lt;li&gt;Pre-render popular articles.&lt;/li&gt;
&lt;li&gt;Refresh the cache periodically.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This reduces API requests while keeping content up to date.&lt;/p&gt;




&lt;h1&gt;
  
  
  Error Handling
&lt;/h1&gt;

&lt;p&gt;Whenever you depend on an external API, you should plan for failures.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;API downtime&lt;/li&gt;
&lt;li&gt;Rate limits&lt;/li&gt;
&lt;li&gt;Network issues&lt;/li&gt;
&lt;li&gt;Invalid responses&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A simple fallback page or cached copy can greatly improve the user experience.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why I Like This Setup
&lt;/h1&gt;

&lt;p&gt;This approach gives me the best of both worlds.&lt;/p&gt;

&lt;p&gt;I get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The reach of DEV.to.&lt;/li&gt;
&lt;li&gt;A fully customized personal website.&lt;/li&gt;
&lt;li&gt;No CMS maintenance.&lt;/li&gt;
&lt;li&gt;No duplicate content management.&lt;/li&gt;
&lt;li&gt;One publishing workflow.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most importantly, I spend my time writing instead of maintaining content in multiple places.&lt;/p&gt;




&lt;h1&gt;
  
  
  Could You Do the Same?
&lt;/h1&gt;

&lt;p&gt;Absolutely.&lt;/p&gt;

&lt;p&gt;If you already publish on DEV.to, you can build your own blog in just a few hours using its public API.&lt;/p&gt;

&lt;p&gt;Whether you're using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Next.js&lt;/li&gt;
&lt;li&gt;React&lt;/li&gt;
&lt;li&gt;Vue&lt;/li&gt;
&lt;li&gt;Nuxt&lt;/li&gt;
&lt;li&gt;Svelte&lt;/li&gt;
&lt;li&gt;Astro&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;the process is almost identical:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Fetch articles.&lt;/li&gt;
&lt;li&gt;Display the list.&lt;/li&gt;
&lt;li&gt;Fetch a single article by slug.&lt;/li&gt;
&lt;li&gt;Render the Markdown.&lt;/li&gt;
&lt;li&gt;Style it however you like.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The API is simple, well documented, and perfect for personal portfolio websites. :contentReference[oaicite:5]{index=5}&lt;/p&gt;




&lt;h1&gt;
  
  
  Final Thoughts
&lt;/h1&gt;

&lt;p&gt;Building my blog around the DEV.to API has been one of the simplest yet most rewarding improvements to my personal website.&lt;/p&gt;

&lt;p&gt;Instead of managing two separate publishing platforms, I now have a single workflow: write once, publish once, and let the API do the rest.&lt;/p&gt;

&lt;p&gt;If you're already sharing technical articles on DEV.to and want a blog on your own domain, I highly recommend giving this approach a try. It's lightweight, developer-friendly, and removes a lot of repetitive work—letting you focus on what matters most: writing great content.&lt;/p&gt;

</description>
      <category>devto</category>
      <category>blogging</category>
      <category>resources</category>
    </item>
    <item>
      <title>What Is Load Balancing? A Beginner's Guide to Faster and More Reliable Applications</title>
      <dc:creator>Mohamad Ashraful Islam</dc:creator>
      <pubDate>Tue, 21 Jul 2026 03:55:49 +0000</pubDate>
      <link>https://dev.to/ashraful/what-is-load-balancing-a-beginners-guide-to-faster-and-more-reliable-applications-nl</link>
      <guid>https://dev.to/ashraful/what-is-load-balancing-a-beginners-guide-to-faster-and-more-reliable-applications-nl</guid>
      <description>&lt;p&gt;Imagine you're standing in line at a supermarket with only one cashier open. The queue is long, and everyone has to wait.&lt;/p&gt;

&lt;p&gt;Now imagine the store opens four more checkout counters. Customers are directed to the shortest queue, and everyone gets served much faster.&lt;/p&gt;

&lt;p&gt;That's exactly how &lt;strong&gt;load balancing&lt;/strong&gt; works in computer networks.&lt;/p&gt;

&lt;p&gt;Instead of sending every user request to a single server, a &lt;strong&gt;load balancer&lt;/strong&gt; distributes requests across multiple servers, ensuring no single machine becomes overloaded.&lt;/p&gt;

&lt;p&gt;In this article, we'll explore what load balancing is, why it's important, how it works, and the different algorithms used to distribute traffic.&lt;/p&gt;




&lt;h1&gt;
  
  
  What Is Load Balancing?
&lt;/h1&gt;

&lt;p&gt;A &lt;strong&gt;load balancer&lt;/strong&gt; is a system that sits between users and your application servers. It receives incoming requests and forwards them to one of several backend servers based on a set of rules.&lt;/p&gt;

&lt;p&gt;Without a load balancer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                 Users
                   │
                   ▼
            +-------------+
            |   Server 1  |
            +-------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Every request goes to the same server.&lt;/p&gt;

&lt;p&gt;If too many users connect at once, the server may slow down or even crash.&lt;/p&gt;

&lt;p&gt;With a load balancer:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                    Users
                      │
                      ▼
            +------------------+
            |  Load Balancer   |
            +------------------+
              │      │      │
      ┌───────┘      │      └────────┐
      ▼              ▼               ▼
+------------+ +------------+ +------------+
|  Server 1  | |  Server 2  | |  Server 3  |
+------------+ +------------+ +------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Requests are shared across all servers.&lt;/p&gt;



&lt;p&gt;If you still confused about scaling part then read the article. Also this article is prerequisite of leaning load balancing. &lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/ashraful/horizontal-vs-vertical-scaling-13h4" class="crayons-story__hidden-navigation-link"&gt;Horizontal vs Vertical Scaling&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/ashraful" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F30490%2F9dc1ef64-0233-43b5-9127-79affd83cc43.jpg" alt="ashraful profile" class="crayons-avatar__image" width="800" height="1067"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/ashraful" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Mohamad Ashraful Islam
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Mohamad Ashraful Islam
                
              
              &lt;div id="story-author-preview-content-462896" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/ashraful" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F30490%2F9dc1ef64-0233-43b5-9127-79affd83cc43.jpg" class="crayons-avatar__image" alt="" width="800" height="1067"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Mohamad Ashraful Islam&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/ashraful/horizontal-vs-vertical-scaling-13h4" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Sep 22 '20&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/ashraful/horizontal-vs-vertical-scaling-13h4" id="article-link-462896"&gt;
          Horizontal vs Vertical Scaling
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/systemdesign"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;systemdesign&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/server"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;server&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/loadbalancing"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;loadbalancing&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/scaling"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;scaling&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/ashraful/horizontal-vs-vertical-scaling-13h4" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;3&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/ashraful/horizontal-vs-vertical-scaling-13h4#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              2&lt;span class="hidden s:inline"&gt;&amp;nbsp;comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            2 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;/div&gt;
&lt;br&gt;





&lt;h1&gt;
  
  
  Why Do We Need Load Balancing?
&lt;/h1&gt;

&lt;p&gt;Modern applications often serve thousands—or even millions—of users.&lt;/p&gt;

&lt;p&gt;If all requests go to one server, it may suffer from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;High CPU usage&lt;/li&gt;
&lt;li&gt;Memory exhaustion&lt;/li&gt;
&lt;li&gt;Slow response times&lt;/li&gt;
&lt;li&gt;Application crashes&lt;/li&gt;
&lt;li&gt;Downtime&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Load balancing helps by spreading traffic evenly, allowing applications to remain fast and available even during heavy usage.&lt;/p&gt;




&lt;h1&gt;
  
  
  A Real-Life Example
&lt;/h1&gt;

&lt;p&gt;Imagine you're running a video streaming service similar to Jellyfin.&lt;/p&gt;

&lt;p&gt;On a normal day:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;20 people are watching videos.&lt;/li&gt;
&lt;li&gt;One server handles the load without problems.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then a popular movie is released.&lt;/p&gt;

&lt;p&gt;Suddenly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;5,000 people start streaming at the same time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without load balancing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;5000 Users
     │
     ▼
Server 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The server becomes overwhelmed.&lt;/p&gt;

&lt;p&gt;With load balancing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;5000 Users
      │
      ▼
+------------------+
|  Load Balancer   |
+------------------+
   │      │      │
   ▼      ▼      ▼
1000    2000   2000 Users
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each server handles only part of the traffic, keeping the service responsive.&lt;/p&gt;




&lt;h1&gt;
  
  
  How Load Balancing Works
&lt;/h1&gt;

&lt;p&gt;Let's walk through a simple request.&lt;/p&gt;

&lt;p&gt;A user visits:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The request reaches the load balancer.&lt;/p&gt;

&lt;p&gt;The load balancer checks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which servers are available?&lt;/li&gt;
&lt;li&gt;Which server has the least load?&lt;/li&gt;
&lt;li&gt;Which routing algorithm is configured?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It then forwards the request to the selected server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User
 │
 ▼
Load Balancer
 │
 ├──► Server 1
 ├──► Server 2
 └──► Server 3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The chosen server processes the request and sends the response back through the load balancer to the user.&lt;/p&gt;

&lt;p&gt;To the user, everything appears as a single website.&lt;/p&gt;




&lt;h1&gt;
  
  
  Load Balancing Algorithms
&lt;/h1&gt;

&lt;p&gt;A load balancer needs a strategy to decide where to send each request.&lt;/p&gt;

&lt;p&gt;Here are some of the most common algorithms.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Round Robin
&lt;/h2&gt;

&lt;p&gt;This is the simplest method.&lt;/p&gt;

&lt;p&gt;Requests are distributed one after another.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Request 1 → Server 1

Request 2 → Server 2

Request 3 → Server 3

Request 4 → Server 1

Request 5 → Server 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Advantages
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Easy to implement&lt;/li&gt;
&lt;li&gt;Fair when servers have similar hardware&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Disadvantages
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Doesn't consider server workload&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  2. Least Connections
&lt;/h2&gt;

&lt;p&gt;The load balancer sends the next request to the server with the fewest active connections.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Server 1
120 Connections

Server 2
25 Connections

Server 3
70 Connections
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next request goes to &lt;strong&gt;Server 2&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This method is useful when requests take different amounts of time to complete.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Least Response Time
&lt;/h2&gt;

&lt;p&gt;Instead of counting connections, this algorithm chooses the server responding the fastest.&lt;/p&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Server&lt;/th&gt;
&lt;th&gt;Response Time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Server 1&lt;/td&gt;
&lt;td&gt;20 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Server 2&lt;/td&gt;
&lt;td&gt;80 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Server 3&lt;/td&gt;
&lt;td&gt;12 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The next request goes to &lt;strong&gt;Server 3&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Weighted Round Robin
&lt;/h2&gt;

&lt;p&gt;Sometimes servers have different hardware.&lt;/p&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Server&lt;/th&gt;
&lt;th&gt;CPU Cores&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Server 1&lt;/td&gt;
&lt;td&gt;32&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Server 2&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Server 3&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Instead of sending the same number of requests to each server, the load balancer assigns &lt;strong&gt;weights&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Server 1
50%

Server 2
30%

Server 3
20%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;More powerful servers receive more traffic.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. IP Hash
&lt;/h2&gt;

&lt;p&gt;Some applications require users to always reach the same server.&lt;/p&gt;

&lt;p&gt;The load balancer hashes the client's IP address.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User A
↓

Server 2

User B
↓

Server 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is useful for applications that maintain user sessions locally.&lt;/p&gt;




&lt;h1&gt;
  
  
  Health Checks
&lt;/h1&gt;

&lt;p&gt;A load balancer continuously checks whether backend servers are healthy.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Server 1 ✓

Server 2 ✗

Server 3 ✓
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;strong&gt;Server 2&lt;/strong&gt; fails, the load balancer automatically stops sending traffic to it.&lt;/p&gt;

&lt;p&gt;Once it recovers, it can be added back into the pool.&lt;/p&gt;

&lt;p&gt;This increases application availability without requiring user intervention.&lt;/p&gt;




&lt;h1&gt;
  
  
  Session Persistence (Sticky Sessions)
&lt;/h1&gt;

&lt;p&gt;Some applications store user session data in memory.&lt;/p&gt;

&lt;p&gt;If requests bounce between servers, users may be logged out unexpectedly.&lt;/p&gt;

&lt;p&gt;Sticky sessions solve this problem by ensuring a user's requests continue to go to the same backend server.&lt;/p&gt;

&lt;p&gt;However, modern applications often store sessions in shared databases or caches like Redis, reducing the need for sticky sessions.&lt;/p&gt;




&lt;h1&gt;
  
  
  Hardware vs Software Load Balancers
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Hardware Load Balancers
&lt;/h2&gt;

&lt;p&gt;Dedicated networking devices used in large enterprise environments.&lt;/p&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;F5 BIG-IP&lt;/li&gt;
&lt;li&gt;Citrix ADC&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Advantages
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Extremely high performance&lt;/li&gt;
&lt;li&gt;Specialized hardware&lt;/li&gt;
&lt;li&gt;Enterprise support&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Disadvantages
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Expensive&lt;/li&gt;
&lt;li&gt;Less flexible&lt;/li&gt;
&lt;li&gt;Requires dedicated equipment&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Software Load Balancers
&lt;/h2&gt;

&lt;p&gt;Run on standard servers or virtual machines.&lt;/p&gt;

&lt;p&gt;Popular options include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Nginx&lt;/li&gt;
&lt;li&gt;HAProxy&lt;/li&gt;
&lt;li&gt;Traefik&lt;/li&gt;
&lt;li&gt;Envoy&lt;/li&gt;
&lt;li&gt;Caddy&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Advantages
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Low cost&lt;/li&gt;
&lt;li&gt;Flexible&lt;/li&gt;
&lt;li&gt;Easy to deploy&lt;/li&gt;
&lt;li&gt;Cloud-friendly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For most home labs, startups, and small businesses, software load balancers are more than sufficient.&lt;/p&gt;




&lt;h1&gt;
  
  
  Load Balancing in Docker
&lt;/h1&gt;

&lt;p&gt;If you're running multiple container instances, a load balancer can distribute requests among them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                Users
                  │
                  ▼
          +----------------+
          |     Nginx      |
          +----------------+
             │     │     │
             ▼     ▼     ▼
       App-1 App-2 App-3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This improves scalability and allows you to update containers with minimal downtime.&lt;/p&gt;




&lt;h1&gt;
  
  
  Load Balancing in Kubernetes
&lt;/h1&gt;

&lt;p&gt;Kubernetes automatically uses load balancing to expose applications.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Internet
     │
     ▼
 Kubernetes Service
     │
 ┌───┼───┐
 ▼   ▼   ▼
Pod  Pod  Pod
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As Pods are added or removed, Kubernetes updates the routing automatically.&lt;/p&gt;




&lt;h1&gt;
  
  
  Benefits of Load Balancing
&lt;/h1&gt;

&lt;p&gt;Using a load balancer offers several advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Improved performance&lt;/li&gt;
&lt;li&gt;Better scalability&lt;/li&gt;
&lt;li&gt;High availability&lt;/li&gt;
&lt;li&gt;Automatic failover&lt;/li&gt;
&lt;li&gt;Easier maintenance&lt;/li&gt;
&lt;li&gt;Reduced downtime&lt;/li&gt;
&lt;li&gt;Better user experience&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It also allows you to perform rolling updates, replacing or upgrading servers one at a time without taking the entire application offline.&lt;/p&gt;




&lt;h1&gt;
  
  
  Common Misconceptions
&lt;/h1&gt;

&lt;h3&gt;
  
  
  "A load balancer makes my application faster."
&lt;/h3&gt;

&lt;p&gt;Not necessarily.&lt;/p&gt;

&lt;p&gt;A load balancer doesn't make an individual server faster. Instead, it distributes traffic so that no single server becomes a bottleneck.&lt;/p&gt;

&lt;h3&gt;
  
  
  "One server is enough forever."
&lt;/h3&gt;

&lt;p&gt;As your application grows, you'll eventually need multiple servers. Designing your application with load balancing in mind makes future scaling much easier.&lt;/p&gt;

&lt;h3&gt;
  
  
  "Load balancing replaces backups."
&lt;/h3&gt;

&lt;p&gt;Load balancing improves availability, not data protection. You still need a proper backup strategy.&lt;/p&gt;




&lt;h1&gt;
  
  
  When Do You Need Load Balancing?
&lt;/h1&gt;

&lt;p&gt;You should consider using load balancing if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your application receives heavy traffic.&lt;/li&gt;
&lt;li&gt;You want high availability.&lt;/li&gt;
&lt;li&gt;You run multiple application servers.&lt;/li&gt;
&lt;li&gt;You need zero-downtime deployments.&lt;/li&gt;
&lt;li&gt;You want automatic failover.&lt;/li&gt;
&lt;li&gt;You expect your application to grow over time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For small personal projects, a single server may be enough. But as demand increases, load balancing becomes an essential part of a scalable architecture.&lt;/p&gt;




&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Load balancing is one of the core technologies behind modern web applications. From streaming platforms and e-commerce websites to cloud services and container orchestration systems, it's what keeps applications fast, responsive, and highly available.&lt;/p&gt;

&lt;p&gt;By intelligently distributing traffic across multiple servers, a load balancer prevents overload, improves reliability, and allows your infrastructure to scale with user demand.&lt;/p&gt;

&lt;p&gt;Whether you're building a home lab, deploying Docker containers, or designing a production system, understanding load balancing is a valuable step toward building resilient and scalable applications.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>beginners</category>
      <category>networking</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Linux Backup Strategies: Beyond Just `rsync`</title>
      <dc:creator>Mohamad Ashraful Islam</dc:creator>
      <pubDate>Mon, 20 Jul 2026 13:39:05 +0000</pubDate>
      <link>https://dev.to/ashraful/linux-backup-strategies-beyond-just-rsync-1995</link>
      <guid>https://dev.to/ashraful/linux-backup-strategies-beyond-just-rsync-1995</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Data loss isn't a matter of &lt;em&gt;if&lt;/em&gt;—it's a matter of &lt;em&gt;when&lt;/em&gt;.&lt;/strong&gt; Whether it's a failing hard drive, accidental deletion, ransomware, or a simple typo in the terminal, every Linux user should have a backup strategy. While &lt;code&gt;rsync&lt;/code&gt; is an incredibly powerful tool, it's only one piece of the puzzle.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In this guide, we'll explore modern Linux backup strategies, understand where &lt;code&gt;rsync&lt;/code&gt; fits in, and build a reliable backup plan that you can trust.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why Backups Matter
&lt;/h1&gt;

&lt;p&gt;Imagine these situations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You accidentally run &lt;code&gt;rm -rf&lt;/code&gt; in the wrong directory.&lt;/li&gt;
&lt;li&gt;Your SSD suddenly dies.&lt;/li&gt;
&lt;li&gt;A Docker volume becomes corrupted.&lt;/li&gt;
&lt;li&gt;Your server is compromised.&lt;/li&gt;
&lt;li&gt;A power outage damages your filesystem.&lt;/li&gt;
&lt;li&gt;Your laptop is stolen.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without backups, recovery may be impossible.&lt;/p&gt;

&lt;p&gt;A good backup strategy protects you from &lt;strong&gt;hardware failure, software bugs, human error, and disasters&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  What Makes a Good Backup?
&lt;/h1&gt;

&lt;p&gt;A reliable backup should be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Automatic&lt;/strong&gt; – Runs without manual intervention.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Versioned&lt;/strong&gt; – Keeps previous versions of files.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Redundant&lt;/strong&gt; – Stored in more than one location.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Encrypted&lt;/strong&gt; – Protects sensitive data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tested&lt;/strong&gt; – Regularly verified by restoring files.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Remember:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A backup that has never been tested is just a theory.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  The 3-2-1 Backup Rule
&lt;/h1&gt;

&lt;p&gt;The most widely recommended backup strategy is the &lt;strong&gt;3-2-1 Rule&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;3 copies&lt;/strong&gt; of your data&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;2 different storage devices&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;1 copy stored off-site&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                Original Data
                      │
          ┌───────────┴───────────┐
          │                       │
          ▼                       ▼
 External SSD              Cloud Storage
          │
          ▼
   Local Backup Copy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This protects against:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Disk failures&lt;/li&gt;
&lt;li&gt;Fire&lt;/li&gt;
&lt;li&gt;Theft&lt;/li&gt;
&lt;li&gt;Flood&lt;/li&gt;
&lt;li&gt;Ransomware&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Understanding &lt;code&gt;rsync&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;&lt;code&gt;rsync&lt;/code&gt; is one of the most popular backup tools on Linux.&lt;/p&gt;

&lt;p&gt;Instead of copying everything every time, it copies &lt;strong&gt;only the changed files&lt;/strong&gt;, making it incredibly efficient.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rsync &lt;span class="nt"&gt;-avh&lt;/span&gt; &lt;span class="nt"&gt;--delete&lt;/span&gt; /home/user/Documents/ /mnt/backup/Documents/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Options explained:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-a&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Archive mode (preserves permissions, timestamps, symlinks, etc.)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-v&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Verbose output&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-h&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Human-readable sizes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--delete&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove files from destination that no longer exist in source&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h1&gt;
  
  
  The Problem with &lt;code&gt;rsync&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;Many people think &lt;code&gt;rsync&lt;/code&gt; &lt;strong&gt;is&lt;/strong&gt; a backup.&lt;/p&gt;

&lt;p&gt;It isn't.&lt;/p&gt;

&lt;p&gt;It's primarily a synchronization tool.&lt;/p&gt;

&lt;p&gt;Suppose you accidentally delete a file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Source
├── photo.jpg ❌ deleted
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rsync &lt;span class="nt"&gt;--delete&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;also deletes the file from your backup.&lt;/p&gt;

&lt;p&gt;Your backup now contains the same mistake.&lt;/p&gt;

&lt;p&gt;This is why relying solely on &lt;code&gt;rsync&lt;/code&gt; can be dangerous.&lt;/p&gt;




&lt;h1&gt;
  
  
  Versioned Backups
&lt;/h1&gt;

&lt;p&gt;Instead of overwriting yesterday's backup, keep multiple versions.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Backups

├── 2026-07-01
├── 2026-07-02
├── 2026-07-03
├── 2026-07-04
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If today's backup contains a corrupted file, you can restore yesterday's version.&lt;/p&gt;

&lt;p&gt;This protects against:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Accidental deletions&lt;/li&gt;
&lt;li&gt;File corruption&lt;/li&gt;
&lt;li&gt;Malware&lt;/li&gt;
&lt;li&gt;Ransomware&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Incremental Backups
&lt;/h1&gt;

&lt;p&gt;An incremental backup stores &lt;strong&gt;only the files that changed since the last backup&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Day 1
100 GB copied

Day 2
Only 500 MB changed

Backup Size
500 MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Faster backups&lt;/li&gt;
&lt;li&gt;Less storage space&lt;/li&gt;
&lt;li&gt;Lower disk usage&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Popular tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;BorgBackup&lt;/li&gt;
&lt;li&gt;Restic&lt;/li&gt;
&lt;li&gt;Duplicity&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Differential Backups
&lt;/h1&gt;

&lt;p&gt;Differential backups store changes since the &lt;strong&gt;last full backup&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Sunday
Full Backup

Monday
Changes since Sunday

Tuesday
Changes since Sunday

Wednesday
Changes since Sunday
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restoring is faster than incremental backups because you only need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One full backup&lt;/li&gt;
&lt;li&gt;One differential backup&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Snapshot-Based Backups
&lt;/h1&gt;

&lt;p&gt;Modern filesystems can create snapshots almost instantly.&lt;/p&gt;

&lt;p&gt;A snapshot captures the state of a filesystem at a specific moment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Filesystem

Snapshot A
Snapshot B
Snapshot C
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Snapshots are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fast&lt;/li&gt;
&lt;li&gt;Space-efficient&lt;/li&gt;
&lt;li&gt;Easy to restore&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Supported by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Btrfs&lt;/li&gt;
&lt;li&gt;ZFS&lt;/li&gt;
&lt;li&gt;LVM&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Snapshots are excellent for protecting against accidental changes, but they &lt;strong&gt;are not backups&lt;/strong&gt; unless stored on a separate device.&lt;/p&gt;




&lt;h1&gt;
  
  
  Archive Backups with &lt;code&gt;tar&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;Sometimes you just want to archive a directory.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-czf&lt;/span&gt; backup.tar.gz /data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For better compression:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-cJf&lt;/span&gt; backup.tar.xz /data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compression comparison:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Format&lt;/th&gt;
&lt;th&gt;Speed&lt;/th&gt;
&lt;th&gt;Compression&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;gzip&lt;/td&gt;
&lt;td&gt;Fast&lt;/td&gt;
&lt;td&gt;Good&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;bzip2&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Better&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;xz&lt;/td&gt;
&lt;td&gt;Slow&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;zstd&lt;/td&gt;
&lt;td&gt;Very Fast&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h1&gt;
  
  
  Remote Backups
&lt;/h1&gt;

&lt;p&gt;Backing up to another machine protects against hardware failure.&lt;/p&gt;

&lt;p&gt;Using &lt;code&gt;rsync&lt;/code&gt; over SSH:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rsync &lt;span class="nt"&gt;-azP&lt;/span&gt; /data user@backup-server:/backup/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Encrypted&lt;/li&gt;
&lt;li&gt;Efficient&lt;/li&gt;
&lt;li&gt;Reliable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For home labs, this is often the easiest way to create an off-site or secondary backup.&lt;/p&gt;




&lt;h1&gt;
  
  
  Cloud Backups
&lt;/h1&gt;

&lt;p&gt;Local backups are great—but what if your house floods or catches fire?&lt;/p&gt;

&lt;p&gt;Cloud storage provides geographic redundancy.&lt;/p&gt;

&lt;p&gt;Popular providers include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Backblaze B2&lt;/li&gt;
&lt;li&gt;Wasabi&lt;/li&gt;
&lt;li&gt;AWS S3&lt;/li&gt;
&lt;li&gt;Google Cloud Storage&lt;/li&gt;
&lt;li&gt;Microsoft Azure&lt;/li&gt;
&lt;li&gt;Dropbox&lt;/li&gt;
&lt;li&gt;Google Drive&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tools like &lt;strong&gt;Restic&lt;/strong&gt; and &lt;strong&gt;Rclone&lt;/strong&gt; make cloud backups straightforward.&lt;/p&gt;




&lt;h1&gt;
  
  
  Encrypt Your Backups
&lt;/h1&gt;

&lt;p&gt;Backups often contain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Passwords&lt;/li&gt;
&lt;li&gt;Photos&lt;/li&gt;
&lt;li&gt;Personal documents&lt;/li&gt;
&lt;li&gt;Financial records&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If someone steals your backup drive, they shouldn't gain access to your data.&lt;/p&gt;

&lt;p&gt;Tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GPG&lt;/li&gt;
&lt;li&gt;LUKS&lt;/li&gt;
&lt;li&gt;Restic (built-in encryption)&lt;/li&gt;
&lt;li&gt;BorgBackup (built-in encryption)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Encryption is especially important for cloud backups and portable drives.&lt;/p&gt;




&lt;h1&gt;
  
  
  Automating Backups
&lt;/h1&gt;

&lt;p&gt;A backup that depends on memory is likely to be forgotten.&lt;/p&gt;

&lt;p&gt;Use automation:&lt;/p&gt;

&lt;h3&gt;
  
  
  Cron
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0 2 * * * /usr/local/bin/backup.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Runs every day at 2:00 AM.&lt;/p&gt;

&lt;h3&gt;
  
  
  Systemd Timers
&lt;/h3&gt;

&lt;p&gt;Systemd timers provide more flexibility than cron, including better logging and missed-job handling.&lt;/p&gt;




&lt;h1&gt;
  
  
  Docker Backup Strategies
&lt;/h1&gt;

&lt;p&gt;If you use Docker, don't just back up the containers.&lt;/p&gt;

&lt;p&gt;Focus on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Docker volumes&lt;/li&gt;
&lt;li&gt;Bind mounts&lt;/li&gt;
&lt;li&gt;Configuration files&lt;/li&gt;
&lt;li&gt;Docker Compose files&lt;/li&gt;
&lt;li&gt;Environment (&lt;code&gt;.env&lt;/code&gt;) files&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;docker-compose.yml&lt;/span&gt;
&lt;span class="s"&gt;.env&lt;/span&gt;
&lt;span class="s"&gt;config/&lt;/span&gt;
&lt;span class="s"&gt;volumes/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Containers can be recreated. Your data cannot.&lt;/p&gt;




&lt;h1&gt;
  
  
  Verify Your Backups
&lt;/h1&gt;

&lt;p&gt;The worst time to discover a broken backup is during an emergency.&lt;/p&gt;

&lt;p&gt;Regularly test your backups.&lt;/p&gt;

&lt;p&gt;Checklist:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Restore random files.&lt;/li&gt;
&lt;li&gt;Verify archive integrity.&lt;/li&gt;
&lt;li&gt;Check file permissions.&lt;/li&gt;
&lt;li&gt;Ensure timestamps are preserved.&lt;/li&gt;
&lt;li&gt;Confirm backups complete successfully.&lt;/li&gt;
&lt;li&gt;Review logs for errors.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Practice recovery before you actually need it.&lt;/p&gt;




&lt;h1&gt;
  
  
  Recommended Backup Tools
&lt;/h1&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;rsync&lt;/td&gt;
&lt;td&gt;File synchronization&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;tar&lt;/td&gt;
&lt;td&gt;Archives&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BorgBackup&lt;/td&gt;
&lt;td&gt;Deduplicated encrypted backups&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Restic&lt;/td&gt;
&lt;td&gt;Cloud backups&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rclone&lt;/td&gt;
&lt;td&gt;Cloud storage synchronization&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Timeshift&lt;/td&gt;
&lt;td&gt;Desktop system snapshots&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Snapper&lt;/td&gt;
&lt;td&gt;Btrfs snapshots&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Duplicity&lt;/td&gt;
&lt;td&gt;Encrypted incremental backups&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h1&gt;
  
  
  A Practical Home Server Backup Strategy
&lt;/h1&gt;

&lt;p&gt;Here's a simple yet effective backup plan for a self-hosted Linux server.&lt;/p&gt;

&lt;h3&gt;
  
  
  Daily
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;rsync&lt;/code&gt; important data to an external SSD.&lt;/li&gt;
&lt;li&gt;Create compressed archives of configuration files.&lt;/li&gt;
&lt;li&gt;Verify backup logs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Weekly
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Sync backups to a remote server over SSH.&lt;/li&gt;
&lt;li&gt;Upload critical data to cloud storage.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Monthly
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Test restoring files.&lt;/li&gt;
&lt;li&gt;Check disk health (SMART).&lt;/li&gt;
&lt;li&gt;Verify backup integrity.&lt;/li&gt;
&lt;li&gt;Remove outdated backups according to your retention policy.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This layered approach protects against accidental deletion, hardware failure, and site-wide disasters.&lt;/p&gt;




&lt;h1&gt;
  
  
  Common Backup Mistakes
&lt;/h1&gt;

&lt;p&gt;Avoid these common pitfalls:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❌ Keeping backups on the same drive.&lt;/li&gt;
&lt;li&gt;❌ Never testing restores.&lt;/li&gt;
&lt;li&gt;❌ Forgetting hidden files.&lt;/li&gt;
&lt;li&gt;❌ Ignoring Docker volumes.&lt;/li&gt;
&lt;li&gt;❌ Storing backups unencrypted.&lt;/li&gt;
&lt;li&gt;❌ Running backups manually.&lt;/li&gt;
&lt;li&gt;❌ Keeping only one backup copy.&lt;/li&gt;
&lt;li&gt;❌ Assuming RAID is a backup.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Remember:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;RAID improves availability—it does not replace backups.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;&lt;code&gt;rsync&lt;/code&gt; is an excellent tool, but it's only one part of a comprehensive backup strategy. A truly reliable backup system combines multiple techniques: synchronization, versioning, encryption, automation, remote storage, and regular testing.&lt;/p&gt;

&lt;p&gt;By following the &lt;strong&gt;3-2-1 rule&lt;/strong&gt;, keeping versioned backups, automating the process, and verifying restores, you can dramatically reduce the risk of losing important data.&lt;/p&gt;

&lt;p&gt;Because in the world of Linux—and computing in general—the best backup isn't the one you make after disaster strikes. It's the one that's already waiting when it does.&lt;/p&gt;

</description>
      <category>rsync</category>
      <category>backup</category>
      <category>linux</category>
      <category>321backup</category>
    </item>
    <item>
      <title>How Reverse Proxies Actually Work (And Why Every Self-Hosted Server Needs One)</title>
      <dc:creator>Mohamad Ashraful Islam</dc:creator>
      <pubDate>Sun, 19 Jul 2026 16:51:42 +0000</pubDate>
      <link>https://dev.to/ashraful/how-reverse-proxies-actually-work-and-why-every-self-hosted-server-needs-one-103d</link>
      <guid>https://dev.to/ashraful/how-reverse-proxies-actually-work-and-why-every-self-hosted-server-needs-one-103d</guid>
      <description>&lt;p&gt;Imagine walking into a large office building. You don't know which department handles your request, so you first stop at the reception desk.&lt;/p&gt;

&lt;p&gt;The receptionist asks what you need, checks your request, and directs you to the correct department.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;reverse proxy&lt;/strong&gt; works exactly like that receptionist.&lt;/p&gt;

&lt;p&gt;Instead of exposing every application directly to the internet, all traffic first reaches the reverse proxy. The reverse proxy then decides where that request should go.&lt;/p&gt;

&lt;p&gt;If you're running a home server, VPS, or Kubernetes cluster, understanding reverse proxies is one of the most valuable networking concepts you can learn.&lt;/p&gt;

&lt;p&gt;Let's dive in.&lt;/p&gt;




&lt;h1&gt;
  
  
  What Is a Reverse Proxy?
&lt;/h1&gt;

&lt;p&gt;A reverse proxy is a server that sits &lt;strong&gt;between clients and your backend applications&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Instead of users connecting directly to your services, they connect to the reverse proxy first.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Internet
     │
     ▼
+--------------------+
|   Reverse Proxy    |
+--------------------+
        │
 ┌──────┼─────────┐
 │      │         │
 ▼      ▼         ▼
Jellyfin Immich  Nextcloud
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The client never talks directly to Jellyfin, Immich, or Nextcloud.&lt;/p&gt;

&lt;p&gt;Everything passes through the reverse proxy.&lt;/p&gt;




&lt;h1&gt;
  
  
  Reverse Proxy vs Forward Proxy
&lt;/h1&gt;

&lt;p&gt;People often confuse these two.&lt;/p&gt;

&lt;h2&gt;
  
  
  Forward Proxy
&lt;/h2&gt;

&lt;p&gt;A forward proxy works &lt;strong&gt;for the client&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You
 │
 ▼
Forward Proxy
 │
 ▼
Internet
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Corporate networks&lt;/li&gt;
&lt;li&gt;School internet&lt;/li&gt;
&lt;li&gt;VPNs&lt;/li&gt;
&lt;li&gt;Anonymous browsing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The website sees the proxy—not you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reverse Proxy
&lt;/h2&gt;

&lt;p&gt;A reverse proxy works &lt;strong&gt;for the server&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Internet
     │
     ▼
Reverse Proxy
     │
     ▼
Your Applications
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Clients don't know which server actually handled the request.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why Not Just Open Every Port?
&lt;/h1&gt;

&lt;p&gt;Suppose your server runs:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Application&lt;/th&gt;
&lt;th&gt;Port&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Jellyfin&lt;/td&gt;
&lt;td&gt;8096&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Immich&lt;/td&gt;
&lt;td&gt;2283&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nextcloud&lt;/td&gt;
&lt;td&gt;8080&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grafana&lt;/td&gt;
&lt;td&gt;3000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Portainer&lt;/td&gt;
&lt;td&gt;9443&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Without a reverse proxy, you'd access them like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://example.com:8096
https://example.com:2283
https://example.com:8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's messy.&lt;/p&gt;

&lt;p&gt;Instead, a reverse proxy lets you use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://movies.example.com
https://photos.example.com
https://cloud.example.com
https://grafana.example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Much cleaner.&lt;/p&gt;




&lt;h1&gt;
  
  
  How a Reverse Proxy Routes Requests
&lt;/h1&gt;

&lt;p&gt;Imagine someone opens:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://movies.example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The request reaches your reverse proxy.&lt;/p&gt;

&lt;p&gt;The reverse proxy checks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Host: movies.example.com
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It finds a rule:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;movies.example.com
      ↓
http://192.168.1.20:8096
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The request is forwarded to Jellyfin.&lt;/p&gt;

&lt;p&gt;If someone instead visits:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://photos.example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The proxy routes it to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://192.168.1.20:2283
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which is your Immich server.&lt;/p&gt;

&lt;p&gt;The client never notices this routing.&lt;/p&gt;




&lt;h1&gt;
  
  
  A Real Example
&lt;/h1&gt;

&lt;p&gt;Suppose your home server hosts four services.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;192.168.10.10

├── Jellyfin
├── Immich
├── Grafana
└── Portainer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You install Nginx Proxy Manager.&lt;/p&gt;

&lt;p&gt;DNS records:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;movies.example.com
photos.example.com
grafana.example.com
portainer.example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Routing table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;movies.example.com
    ↓
localhost:8096

photos.example.com
    ↓
localhost:2283

grafana.example.com
    ↓
localhost:3000

portainer.example.com
    ↓
localhost:9443
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything is now accessible using simple URLs.&lt;/p&gt;




&lt;h1&gt;
  
  
  HTTPS Becomes Easy
&lt;/h1&gt;

&lt;p&gt;Without a reverse proxy, every application must:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generate certificates&lt;/li&gt;
&lt;li&gt;Renew certificates&lt;/li&gt;
&lt;li&gt;Configure HTTPS&lt;/li&gt;
&lt;li&gt;Handle redirects&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's a lot of work.&lt;/p&gt;

&lt;p&gt;Instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Internet
    │ HTTPS
    ▼
Reverse Proxy
    │ HTTP
    ▼
Applications
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The reverse proxy handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SSL certificates&lt;/li&gt;
&lt;li&gt;Let's Encrypt&lt;/li&gt;
&lt;li&gt;Automatic renewal&lt;/li&gt;
&lt;li&gt;HTTPS redirects&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your backend applications can remain on HTTP inside your trusted local network.&lt;/p&gt;




&lt;h1&gt;
  
  
  Security Benefits
&lt;/h1&gt;

&lt;p&gt;A reverse proxy isn't just about convenience.&lt;/p&gt;

&lt;p&gt;It adds another security layer.&lt;/p&gt;

&lt;p&gt;It can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hide internal IP addresses&lt;/li&gt;
&lt;li&gt;Block malicious requests&lt;/li&gt;
&lt;li&gt;Limit request rates&lt;/li&gt;
&lt;li&gt;Require authentication&lt;/li&gt;
&lt;li&gt;Restrict countries&lt;/li&gt;
&lt;li&gt;Filter bad bots&lt;/li&gt;
&lt;li&gt;Prevent direct access to services&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your applications become much harder to attack.&lt;/p&gt;




&lt;h1&gt;
  
  
  Load Balancing
&lt;/h1&gt;

&lt;p&gt;Suppose one server isn't enough.&lt;/p&gt;

&lt;p&gt;Instead of one backend:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;App Server 1
App Server 2
App Server 3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The reverse proxy distributes traffic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client 1 → Server 1

Client 2 → Server 2

Client 3 → Server 3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Higher availability&lt;/li&gt;
&lt;li&gt;Better performance&lt;/li&gt;
&lt;li&gt;Fault tolerance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Large websites rely heavily on this.&lt;/p&gt;




&lt;h1&gt;
  
  
  Caching
&lt;/h1&gt;

&lt;p&gt;Imagine your homepage receives thousands of requests.&lt;/p&gt;

&lt;p&gt;Instead of asking your application every time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User
  │
  ▼
Reverse Proxy
  │
Cache Exists?
  │
 Yes ─────► Return Cached Page
  │
 No
  ▼
Application
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Static content like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Images&lt;/li&gt;
&lt;li&gt;CSS&lt;/li&gt;
&lt;li&gt;JavaScript&lt;/li&gt;
&lt;li&gt;Fonts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;can be served directly from the reverse proxy.&lt;/p&gt;

&lt;p&gt;This greatly reduces application load.&lt;/p&gt;




&lt;h1&gt;
  
  
  Compression
&lt;/h1&gt;

&lt;p&gt;Modern reverse proxies can compress responses.&lt;/p&gt;

&lt;p&gt;Instead of sending:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTML
CSS
JavaScript
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;uncompressed, they use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Gzip&lt;/li&gt;
&lt;li&gt;Brotli&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Smaller responses mean:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Faster websites&lt;/li&gt;
&lt;li&gt;Lower bandwidth usage&lt;/li&gt;
&lt;li&gt;Better user experience&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  WebSockets and Streaming
&lt;/h1&gt;

&lt;p&gt;Applications like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Jellyfin&lt;/li&gt;
&lt;li&gt;Home Assistant&lt;/li&gt;
&lt;li&gt;Immich&lt;/li&gt;
&lt;li&gt;VS Code Server&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;use persistent connections.&lt;/p&gt;

&lt;p&gt;Reverse proxies understand WebSockets and keep these connections alive while still handling routing and HTTPS.&lt;/p&gt;




&lt;h1&gt;
  
  
  Popular Reverse Proxy Software
&lt;/h1&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Software&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Nginx&lt;/td&gt;
&lt;td&gt;High performance and flexibility&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nginx Proxy Manager&lt;/td&gt;
&lt;td&gt;Easy web interface for beginners&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Traefik&lt;/td&gt;
&lt;td&gt;Docker and Kubernetes integration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HAProxy&lt;/td&gt;
&lt;td&gt;High-performance load balancing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Caddy&lt;/td&gt;
&lt;td&gt;Automatic HTTPS with minimal configuration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Envoy&lt;/td&gt;
&lt;td&gt;Large-scale microservices and cloud-native deployments&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Each has its strengths, but for many self-hosters, &lt;strong&gt;Nginx Proxy Manager&lt;/strong&gt; provides an excellent balance of simplicity and features.&lt;/p&gt;




&lt;h1&gt;
  
  
  Common Mistakes
&lt;/h1&gt;

&lt;p&gt;Many beginners accidentally:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Expose application ports directly to the internet.&lt;/li&gt;
&lt;li&gt;Skip HTTPS entirely.&lt;/li&gt;
&lt;li&gt;Open unnecessary firewall ports.&lt;/li&gt;
&lt;li&gt;Forget to configure &lt;code&gt;X-Forwarded-For&lt;/code&gt; and &lt;code&gt;X-Forwarded-Proto&lt;/code&gt; headers.&lt;/li&gt;
&lt;li&gt;Leave administrative interfaces like Portainer publicly accessible without authentication.&lt;/li&gt;
&lt;li&gt;Mix internal and external DNS records in ways that create routing issues.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Taking time to configure your reverse proxy correctly can prevent these problems.&lt;/p&gt;




&lt;h1&gt;
  
  
  When Do You Need a Reverse Proxy?
&lt;/h1&gt;

&lt;p&gt;You probably need one if you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Host more than one web application.&lt;/li&gt;
&lt;li&gt;Want friendly domain names instead of port numbers.&lt;/li&gt;
&lt;li&gt;Need HTTPS certificates.&lt;/li&gt;
&lt;li&gt;Run Docker containers.&lt;/li&gt;
&lt;li&gt;Self-host services at home.&lt;/li&gt;
&lt;li&gt;Want a central place to manage routing, security, and access control.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For many self-hosted environments, a reverse proxy quickly becomes an essential part of the stack rather than an optional extra.&lt;/p&gt;




&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;A reverse proxy is much more than a traffic router. It acts as the front door to your infrastructure, deciding where requests go, protecting your applications, managing HTTPS, balancing traffic, and improving performance through caching and compression.&lt;/p&gt;

&lt;p&gt;Whether you're running a single Raspberry Pi with a few services or a rack of servers in a data center, the underlying concept is the same:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Clients talk to the reverse proxy, and the reverse proxy talks to your applications.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once you understand this pattern, you'll notice it everywhere—from home labs and Docker setups to cloud platforms and some of the world's largest websites. It is one of the foundational building blocks of modern web infrastructure.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>nginx</category>
      <category>linux</category>
      <category>proxy</category>
    </item>
    <item>
      <title>Celery Tasks on RabbitMQ 4.0: Classic Queues vs Quorum Queues</title>
      <dc:creator>Mohamad Ashraful Islam</dc:creator>
      <pubDate>Mon, 13 Jul 2026 04:59:50 +0000</pubDate>
      <link>https://dev.to/ashraful/celery-tasks-on-rabbitmq-40-classic-queues-vs-quorum-queues-4bie</link>
      <guid>https://dev.to/ashraful/celery-tasks-on-rabbitmq-40-classic-queues-vs-quorum-queues-4bie</guid>
      <description>&lt;h1&gt;
  
  
  Celery Tasks on RabbitMQ 4.0: Classic Queues vs Quorum Queues
&lt;/h1&gt;

&lt;p&gt;When building distributed systems with &lt;strong&gt;Celery&lt;/strong&gt; and &lt;strong&gt;RabbitMQ&lt;/strong&gt;, one of the most common pitfalls is &lt;strong&gt;tasks disappearing&lt;/strong&gt;. This usually happens in clustered setups when using &lt;strong&gt;classic queues&lt;/strong&gt;. RabbitMQ 4.x introduced some changes and new queue types, which affects Celery users in production. In this post, we'll explore:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How RabbitMQ cluster connections work
&lt;/li&gt;
&lt;li&gt;Differences between classic and quorum queues
&lt;/li&gt;
&lt;li&gt;Limitations of each queue type
&lt;/li&gt;
&lt;li&gt;Why tasks sometimes disappear on classic queues in a 3-node cluster
&lt;/li&gt;
&lt;li&gt;How to fix these issues
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  RabbitMQ Cluster Architecture (n/2 + 1)
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;RabbitMQ cluster&lt;/strong&gt; is a peer-to-peer network of nodes where each node can accept connections and coordinate with other nodes. In production, cluster operations often rely on a &lt;strong&gt;majority quorum rule&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Minimum nodes to operate = floor(n/2) + 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example, in a &lt;strong&gt;3-node cluster&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;n = 3&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;(3 / 2) + 1 = 2&lt;/code&gt; nodes
&lt;/li&gt;
&lt;li&gt;At least &lt;strong&gt;2 nodes must be up and reachable&lt;/strong&gt; for the cluster to accept writes or form a majority.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This ensures &lt;strong&gt;consistency and reliability&lt;/strong&gt;, especially for replicated queues.&lt;/p&gt;

&lt;h3&gt;
  
  
  RabbitMQ 3-node cluster diagram
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;        +------------+
        | Node A     |
        | (rabbitmq1)|
        +------------+
           |    \
           |     \
           |      \
        +------------+        +------------+
        | Node B     |--------| Node C     |
        | (rabbitmq2)|        | (rabbitmq3)|
        +------------+        +------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Each node is a &lt;strong&gt;disc node&lt;/strong&gt; (stores persistent queues).
&lt;/li&gt;
&lt;li&gt;Messages in &lt;strong&gt;classic queues&lt;/strong&gt; are stored on the node where the queue is declared.
&lt;/li&gt;
&lt;li&gt;Cluster nodes maintain peer-to-peer links; &lt;code&gt;(n/2)+1&lt;/code&gt; nodes are needed to maintain consistency in operations like leader election.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Classic Queues in RabbitMQ 4.x
&lt;/h2&gt;

&lt;p&gt;Classic queues are the &lt;strong&gt;original queue type&lt;/strong&gt; in RabbitMQ:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Messages are &lt;strong&gt;stored on the node where the queue is created&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;By default, &lt;strong&gt;they are not replicated&lt;/strong&gt; across nodes (unless mirrored, but mirrored queues are deprecated and removed from 4.x ((&lt;a href="https://www.rabbitmq.com/docs/3.13/ha)%5BRead" rel="noopener noreferrer"&gt;https://www.rabbitmq.com/docs/3.13/ha)[Read&lt;/a&gt; more]))
&lt;/li&gt;
&lt;li&gt;Suitable for &lt;strong&gt;single-node setups&lt;/strong&gt; or non-critical workloads&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What changed in RabbitMQ 4.0
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;classic_mirrored_queue_version&lt;/code&gt; feature is &lt;strong&gt;removed&lt;/strong&gt; in 4.0

&lt;ul&gt;
&lt;li&gt;This means old mirrored queues are deprecated
&lt;/li&gt;
&lt;li&gt;Classic queues are now &lt;strong&gt;always node-local unless manually mirrored via legacy options&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why tasks disappear in a classic queue
&lt;/h3&gt;

&lt;p&gt;In a &lt;strong&gt;3-node cluster&lt;/strong&gt;, consider a task published to a &lt;strong&gt;classic queue&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Queue lives on &lt;strong&gt;Node A&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Cluster requires &lt;code&gt;(n/2)+1 = 2&lt;/code&gt; nodes to operate correctly&lt;/li&gt;
&lt;li&gt;If Node A becomes temporarily unreachable (network glitch, maintenance), Celery can still publish tasks to Node B&lt;/li&gt;
&lt;li&gt;Node B cannot deliver the task because &lt;strong&gt;queue master is on Node A&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;As a result, tasks &lt;strong&gt;appear lost&lt;/strong&gt;, especially in short outages&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Example scenario
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Producer publishes task -&amp;gt; Node B
Queue master       -&amp;gt; Node A
Cluster majority    -&amp;gt; 2 nodes required (A + B)
Node A unavailable for short time
Result: Task cannot be delivered, disappears
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;The main reason for disappearing tasks is that &lt;strong&gt;classic queues exist on a single node&lt;/strong&gt;, while the cluster is enforcing majority &lt;code&gt;(n/2)+1&lt;/code&gt;. If the node hosting the queue is unreachable, even briefly, the task cannot be routed to the master.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Quorum Queues
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Quorum queues&lt;/strong&gt; were introduced to replace mirrored queues and solve the reliability problems of classic queues in clusters.&lt;/p&gt;

&lt;h3&gt;
  
  
  How quorum queues work
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Messages are &lt;strong&gt;replicated across multiple nodes&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Writes require &lt;strong&gt;majority acknowledgment&lt;/strong&gt; (consensus) to be considered successful
&lt;/li&gt;
&lt;li&gt;Safe against &lt;strong&gt;node failures&lt;/strong&gt;, because tasks exist on multiple nodes
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Quorum queue diagram in 3-node cluster
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;             +----------------------+
             |      Task Producer    |
             +----------+-----------+
                        |
                        v
             +----------------------+
             | Quorum Queue Leader   |
             |       Node A         |
             +----------+-----------+
                        |
           -------------------------------
           |                             |
           v                             v
+----------------------+       +----------------------+
| Quorum Queue Follower |       | Quorum Queue Follower |
|       Node B          |       |       Node C          |
+----------------------+       +----------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;A task is only removed when &lt;strong&gt;leader + majority followers confirm&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Even if Node A goes down, Node B or C can continue serving the queue&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Limitations of quorum queues
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Limitation&lt;/th&gt;
&lt;th&gt;Explanation&lt;/th&gt;
&lt;th&gt;How to mitigate&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Slower than classic queues&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Each write requires majority acknowledgment&lt;/td&gt;
&lt;td&gt;Use quorum queues only for critical tasks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;No priorities&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Quorum queues do not support task priorities&lt;/td&gt;
&lt;td&gt;Use separate queues for priority handling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Memory pressure&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Replication consumes memory on all nodes&lt;/td&gt;
&lt;td&gt;Monitor memory and adjust &lt;code&gt;ram nodes&lt;/code&gt; if needed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Prefetching&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Workers must fetch 1 task at a time for reliability&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;worker_prefetch_multiplier = 1&lt;/code&gt; in Celery&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  Celery configuration for quorum queues
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;broker_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;amqp://admin:admin@rabbitmq1:5672//&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;task_default_queue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;celery&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;task_default_queue_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quorum&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;task_acks_late&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
&lt;span class="n"&gt;task_reject_on_worker_lost&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
&lt;span class="n"&gt;worker_prefetch_multiplier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Ensures &lt;strong&gt;tasks are not lost&lt;/strong&gt; even if nodes fail
&lt;/li&gt;
&lt;li&gt;Compatible with a RabbitMQ cluster
&lt;/li&gt;
&lt;li&gt;Works reliably with &lt;strong&gt;countdown/ETA tasks&lt;/strong&gt; when paired with separate queues&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Classic vs Quorum: Summary Table
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Classic Queue&lt;/th&gt;
&lt;th&gt;Quorum Queue&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Persistence&lt;/td&gt;
&lt;td&gt;Node-local, disk&lt;/td&gt;
&lt;td&gt;Node-local, replicated to majority&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reliability&lt;/td&gt;
&lt;td&gt;Low in clusters&lt;/td&gt;
&lt;td&gt;High, survives node failures&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Supports ETA / countdown&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Priorities&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Throughput&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Lower than classic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Recommended for production&lt;/td&gt;
&lt;td&gt;❌ Only small setups&lt;/td&gt;
&lt;td&gt;✅ Critical tasks&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Classic queues are node-local in RabbitMQ 4.0&lt;/strong&gt;, so tasks can disappear if the master node is unavailable.
&lt;/li&gt;
&lt;li&gt;RabbitMQ clusters enforce &lt;strong&gt;&lt;code&gt;(n/2)+1&lt;/code&gt; majority&lt;/strong&gt; for operations, which interacts poorly with classic queues on a single node.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quorum queues replicate tasks across nodes&lt;/strong&gt;, making task loss nearly impossible.
&lt;/li&gt;
&lt;li&gt;Celery settings like &lt;code&gt;task_acks_late&lt;/code&gt; and &lt;code&gt;worker_prefetch_multiplier&lt;/code&gt; must be tuned with quorum queues.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Countdown / ETA tasks&lt;/strong&gt; should be isolated in separate queues to avoid blocking main workers.
&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Recommended Production Setup (3-node cluster)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Celery workers -&amp;gt; connect to all 3 nodes
Default queue -&amp;gt; Quorum queue
Countdown queue -&amp;gt; Quorum queue, separate worker
Classic queues -&amp;gt; Only for dev or non-critical workloads
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;This configuration ensures tasks are &lt;strong&gt;never lost&lt;/strong&gt;, even during node failure or cluster partitions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This setup is &lt;strong&gt;robust and production-ready&lt;/strong&gt; for Celery + RabbitMQ 4.0 clusters.&lt;/p&gt;

</description>
      <category>celery</category>
      <category>rabbitmq</category>
      <category>python</category>
      <category>webdev</category>
    </item>
    <item>
      <title>AsyncIO Task Runner (Coro Runner): Simplifying Concurrent Python Task Management</title>
      <dc:creator>Mohamad Ashraful Islam</dc:creator>
      <pubDate>Fri, 05 Dec 2025 05:36:19 +0000</pubDate>
      <link>https://dev.to/ashraful/asyncio-task-runner-coro-runner-simplifying-concurrent-python-task-management-1ho3</link>
      <guid>https://dev.to/ashraful/asyncio-task-runner-coro-runner-simplifying-concurrent-python-task-management-1ho3</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Managing asynchronous tasks in Python can be complex, especially when you need fine-grained control over concurrency limits and task scheduling. Enter &lt;strong&gt;Coro Runner&lt;/strong&gt; (async-coro-runner), a lightweight Python utility that makes concurrent asynchronous task management straightforward and efficient.&lt;/p&gt;

&lt;p&gt;In this post, we'll explore what makes Coro Runner unique, how to use it, and why it might be the perfect solution for your async workload management needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Coro Runner?
&lt;/h2&gt;

&lt;p&gt;Coro Runner is a Python library built on top of Python's native &lt;code&gt;asyncio&lt;/code&gt; module that provides a simple yet powerful interface for managing concurrent asynchronous tasks. It's designed to simplify the execution of multiple async tasks with configurable concurrency limits, all within a single-threaded environment.&lt;/p&gt;

&lt;p&gt;The library is particularly useful when you need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Execute multiple async tasks concurrently with controlled parallelism&lt;/li&gt;
&lt;li&gt;Manage task queues with different priorities&lt;/li&gt;
&lt;li&gt;Avoid overwhelming resources with too many concurrent operations&lt;/li&gt;
&lt;li&gt;Schedule tasks dynamically from anywhere in your application&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key Features
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. &lt;strong&gt;Configurable Concurrency&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Define exactly how many tasks should run simultaneously, preventing resource exhaustion while maximizing throughput.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. &lt;strong&gt;Simple API&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Add tasks from anywhere in your codebase with a straightforward interface that doesn't require deep asyncio knowledge.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. &lt;strong&gt;Worker Queue System&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Multiple queues can be configured with their own priority levels, allowing sophisticated task management.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. &lt;strong&gt;Built on asyncio&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Leverages Python's battle-tested asyncio module (introduced in Python 3.4), ensuring stability and compatibility.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;Getting started is as simple as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;coro-runner
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Quick Start Guide
&lt;/h2&gt;

&lt;p&gt;Here's a basic example to get you started:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;coro_runner&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;CoroRunner&lt;/span&gt;

&lt;span class="c1"&gt;# Initialize the runner with a concurrency limit of 10
&lt;/span&gt;&lt;span class="n"&gt;runner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CoroRunner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;concurrency&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Define an async task
&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Your async processing logic here
&lt;/span&gt;    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;some_async_operation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Processed &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;item_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# Add tasks from anywhere in your code
&lt;/span&gt;&lt;span class="n"&gt;runner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;process_data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;priority&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;high&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="n"&gt;runner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;process_data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;priority&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;low&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="n"&gt;runner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;process_data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;priority&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;high&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt;: Your task function must be an async function (defined with &lt;code&gt;async def&lt;/code&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-World Use Case: FastAPI Integration
&lt;/h2&gt;

&lt;p&gt;One of the most powerful applications of Coro Runner is in web applications. Here's an example using FastAPI to handle background tasks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;coro_runner&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;CoroRunner&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Initialize runner once at startup
&lt;/span&gt;&lt;span class="n"&gt;runner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CoroRunner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;concurrency&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;background_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Simulate some async work
&lt;/span&gt;    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Task &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;task_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; completed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/fire-task&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;trigger_tasks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Schedule multiple tasks
&lt;/span&gt;    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;runner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;background_task&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Scheduled &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; tasks&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;uvicorn&lt;/span&gt;
    &lt;span class="n"&gt;uvicorn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.0.0.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why Choose Coro Runner?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Compared to Raw asyncio
&lt;/h3&gt;

&lt;p&gt;While Python's asyncio is powerful, managing task concurrency often requires boilerplate code. Coro Runner abstracts this complexity:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Without Coro Runner:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Managing concurrency manually with asyncio
&lt;/span&gt;&lt;span class="n"&gt;semaphore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Semaphore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;limited_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task_func&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;semaphore&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;task_func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Need to manage the semaphore for every task...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;With Coro Runner:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Simple and clean
&lt;/span&gt;&lt;span class="n"&gt;runner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CoroRunner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;concurrency&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;runner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;your_task&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Declare Once, Use Everywhere
&lt;/h3&gt;

&lt;p&gt;A significant advantage is that you can initialize the runner once and call it from anywhere in your application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# config.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;coro_runner&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;CoroRunner&lt;/span&gt;
&lt;span class="n"&gt;runner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CoroRunner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;concurrency&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# module_a.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;runner&lt;/span&gt;
&lt;span class="n"&gt;runner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task_a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="c1"&gt;# module_b.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;runner&lt;/span&gt;
&lt;span class="n"&gt;runner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task_b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Advanced Features (Coming Soon)
&lt;/h2&gt;

&lt;p&gt;The Coro Runner roadmap includes exciting enhancements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Monitoring Tool Integration&lt;/strong&gt;: Real-time task monitoring and analytics&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Low-Level API&lt;/strong&gt;: Advanced features like callbacks, acknowledgments, and error handling&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Robust Logging&lt;/strong&gt;: Detailed execution tracking for debugging and optimization&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Use Cases
&lt;/h2&gt;

&lt;p&gt;Coro Runner shines in scenarios such as:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Web Scraping&lt;/strong&gt;: Fetch multiple URLs concurrently while respecting rate limits&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Processing&lt;/strong&gt;: Process large datasets with controlled parallelism&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API Integration&lt;/strong&gt;: Call multiple external APIs without overwhelming them&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Background Jobs&lt;/strong&gt;: Queue and execute background tasks in web applications&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Batch Operations&lt;/strong&gt;: Perform bulk operations with controlled concurrency&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Example: Controlled Web Scraping
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;aiohttp&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;coro_runner&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;CoroRunner&lt;/span&gt;

&lt;span class="n"&gt;runner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CoroRunner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;concurrency&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Only 5 concurrent requests
&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;aiohttp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ClientSession&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Schedule 100 URLs but only 5 will run at once
&lt;/span&gt;&lt;span class="n"&gt;urls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.example.com/data/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;urls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;runner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fetch_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Development and Contribution
&lt;/h2&gt;

&lt;p&gt;Coro Runner is actively developed and welcomes contributions. The project uses Uv for dependency management and includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Comprehensive test suite with pytest&lt;/li&gt;
&lt;li&gt;GitHub Actions for CI/CD&lt;/li&gt;
&lt;li&gt;Docker support for isolated testing&lt;/li&gt;
&lt;li&gt;Example applications demonstrating real-world usage&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To contribute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Clone the repository&lt;/span&gt;
git clone https://github.com/iashraful/async-coro-runner.git
&lt;span class="nb"&gt;cd &lt;/span&gt;async-coro-runner

&lt;span class="c"&gt;# Install dependencies&lt;/span&gt;
uv &lt;span class="nb"&gt;sync&lt;/span&gt;

&lt;span class="c"&gt;# Run tests&lt;/span&gt;
uv run pytest &lt;span class="nt"&gt;-s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Project Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Repository&lt;/strong&gt;: &lt;a href="https://github.com/iashraful/async-coro-runner" rel="noopener noreferrer"&gt;iashraful/async-coro-runner&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PyPI Package&lt;/strong&gt;: &lt;a href="https://pypi.org/project/coro-runner/" rel="noopener noreferrer"&gt;coro-runner&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documentation&lt;/strong&gt;: &lt;a href="https://github.com/iashraful/async-coro-runner/tree/main/coro_runner/docs/docs.md" rel="noopener noreferrer"&gt;Full Documentation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Author&lt;/strong&gt;: &lt;a href="https://ashraful.dev" rel="noopener noreferrer"&gt;Ashraful Islam&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Python 3.12 or later&lt;/li&gt;
&lt;li&gt;uv (for development)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Coro Runner provides a clean, simple abstraction over asyncio's task management, making it easier to build high-performance concurrent applications in Python. Whether you're building web scrapers, API integrations, or background job processors, Coro Runner helps you manage concurrency without the complexity.&lt;/p&gt;

&lt;p&gt;The library's philosophy is clear: provide powerful concurrency control with the simplest possible API. With its growing feature set and active development, Coro Runner is positioned to become an essential tool for Python developers working with asynchronous code.&lt;/p&gt;

&lt;p&gt;Give it a try in your next project, and experience the simplicity of managed async task execution!&lt;/p&gt;




&lt;h2&gt;
  
  
  Getting Help
&lt;/h2&gt;

&lt;p&gt;Have questions or running into issues? Here's how to get help:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Issues&lt;/strong&gt;: &lt;a href="https://github.com/iashraful/async-coro-runner/issues" rel="noopener noreferrer"&gt;GitHub Issues&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Discussions&lt;/strong&gt;: Engage with the community on GitHub&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documentation&lt;/strong&gt;: Check the &lt;a href="https://github.com/iashraful/async-coro-runner/tree/main/coro_runner/docs" rel="noopener noreferrer"&gt;docs folder&lt;/a&gt; for detailed guides&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Stay Updated
&lt;/h2&gt;

&lt;p&gt;⭐ Star the repository on GitHub to stay updated with the latest features and improvements!&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Have you used Coro Runner in your projects? Share your experience in the comments below!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>python</category>
      <category>asyncio</category>
      <category>programming</category>
    </item>
    <item>
      <title>Building a Rock-Solid Home Server Backup Strategy - My 3-2-1 Approach</title>
      <dc:creator>Mohamad Ashraful Islam</dc:creator>
      <pubDate>Tue, 05 Aug 2025 17:24:55 +0000</pubDate>
      <link>https://dev.to/ashraful/building-a-rock-solid-home-server-backup-strategy-my-3-2-1-approach-bd7</link>
      <guid>https://dev.to/ashraful/building-a-rock-solid-home-server-backup-strategy-my-3-2-1-approach-bd7</guid>
      <description>&lt;p&gt;When you’re running a home server that stores your personal files, photos, media, and even services like Pi-hole or Jellyfin, &lt;strong&gt;a proper backup strategy isn’t optional—it’s essential&lt;/strong&gt;. Data loss can happen due to hardware failure, accidental deletion, or even corruption. That’s why I follow a proven and practical method known as the &lt;strong&gt;3-2-1 backup strategy&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In this blog post, I’ll walk you through how I’ve implemented this strategy using a combination of &lt;strong&gt;external SSD storage&lt;/strong&gt; and &lt;strong&gt;network-based backups over SSH&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Originally Posted on &lt;a href="https://blog.ashraful.dev/series/home-server/3-321-backup-strategy-of-home-server.html" rel="noopener noreferrer"&gt;Ashraful's Blog&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What is the 3-2-1 Backup Strategy?
&lt;/h2&gt;

&lt;p&gt;The 3-2-1 rule is a simple yet powerful backup principle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;3&lt;/strong&gt; copies of your data (1 primary + 2 backups)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2&lt;/strong&gt; different types of media (e.g., SSD, HDD, cloud, etc.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;1&lt;/strong&gt; copy stored offsite&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This strategy reduces the risk of total data loss by spreading your backups across multiple locations and types of storage.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Primary Storage: My Home Server
&lt;/h2&gt;

&lt;p&gt;My home server is the heart of my digital setup. It hosts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A media server (Jellyfin)&lt;/li&gt;
&lt;li&gt;Personal files and documents&lt;/li&gt;
&lt;li&gt;Photos and videos (via Immich)&lt;/li&gt;
&lt;li&gt;Various self-hosted services (like Pi-hole, Portainer, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is where the &lt;strong&gt;primary copy&lt;/strong&gt; of all my data lives.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Local Backup: External SSD with Rsync Script
&lt;/h2&gt;

&lt;p&gt;To maintain a local backup, I’ve connected an &lt;strong&gt;external SSD&lt;/strong&gt; directly to my home server.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The SSD is mounted and automatically recognized on boot.&lt;/li&gt;
&lt;li&gt;I back up &lt;strong&gt;everything important&lt;/strong&gt; to this drive using a Bash script powered by &lt;code&gt;rsync&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A cron job or systemd timer can automate the execution of this script regularly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s a sample of the SSD backup script:&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="nv"&gt;SOURCE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/data"&lt;/span&gt;
&lt;span class="nv"&gt;DEST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/mnt/backup_ssd"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Starting backup to external SSD..."&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DEST&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
rsync &lt;span class="nt"&gt;-azP&lt;/span&gt; &lt;span class="nt"&gt;--delete&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SOURCE&lt;/span&gt;&lt;span class="s2"&gt;/"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DEST&lt;/span&gt;&lt;span class="s2"&gt;/"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Backup to SSD completed."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Here is the actual script of mine. &lt;a href="https://github.com/iashraful/backup-on-disk" rel="noopener noreferrer"&gt;Github Repo&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This makes sure the SSD always has the latest version of my data and also removes deleted files for consistency.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Network Backup: Remote Sync Over SSH with Variables
&lt;/h2&gt;

&lt;p&gt;To fulfill the &lt;strong&gt;offsite&lt;/strong&gt; part of the 3-2-1 strategy, I’ve also implemented a &lt;strong&gt;remote backup over my local network&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Here’s how it works:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I use a custom Bash script that uses &lt;code&gt;rsync&lt;/code&gt; over SSH.&lt;/li&gt;
&lt;li&gt;It pulls variables like source, destination, remote user, and host from the script, making it easy to reuse or tweak.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s a sample of the SSH-based backup script:&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="nv"&gt;SOURCE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/data"&lt;/span&gt;
&lt;span class="nv"&gt;REMOTE_USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"user"&lt;/span&gt;
&lt;span class="nv"&gt;REMOTE_HOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"192.168.1.10"&lt;/span&gt;
&lt;span class="nv"&gt;REMOTE_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/mnt/backup/data"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Starting remote network backup..."&lt;/span&gt;
rsync &lt;span class="nt"&gt;-azP&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; ssh &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SOURCE&lt;/span&gt;&lt;span class="s2"&gt;/"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$REMOTE_USER&lt;/span&gt;&lt;span class="s2"&gt;@&lt;/span&gt;&lt;span class="nv"&gt;$REMOTE_HOST&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;$REMOTE_PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Remote backup completed."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures I’m keeping a secondary backup in a different physical location, even if it's just across the network.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Setup Works for Me
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Redundancy&lt;/strong&gt;: With multiple local and remote backups, I have peace of mind even if one disk fails.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Speed&lt;/strong&gt;: The SSD makes for quick restores, while SSH syncs avoid re-copying everything unnecessarily.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Privacy&lt;/strong&gt;: Everything stays in my own network—no cloud involved.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Implementing a backup strategy might seem tedious at first, but trust me—it’s worth every bit of effort. Data loss can be painful and costly, especially when it comes to irreplaceable personal content.&lt;/p&gt;

&lt;p&gt;By following the &lt;strong&gt;3-2-1 strategy&lt;/strong&gt; and using a mix of &lt;strong&gt;external SSDs&lt;/strong&gt; and &lt;strong&gt;network-based backups&lt;/strong&gt;, I’ve built a system that’s simple, reliable, and tailored to my setup. If you’re running a home server, I highly recommend investing time in your own backup plan—your future self will thank you.&lt;/p&gt;

</description>
      <category>backup</category>
      <category>server</category>
      <category>ubuntu</category>
      <category>devops</category>
    </item>
    <item>
      <title>Setting Up Pi-hole in Docker with Proper DNS Configuration</title>
      <dc:creator>Mohamad Ashraful Islam</dc:creator>
      <pubDate>Tue, 05 Aug 2025 05:55:28 +0000</pubDate>
      <link>https://dev.to/ashraful/setting-up-pi-hole-in-docker-with-proper-dns-configuration-2hhb</link>
      <guid>https://dev.to/ashraful/setting-up-pi-hole-in-docker-with-proper-dns-configuration-2hhb</guid>
      <description>&lt;h2&gt;
  
  
  This guide walks through
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Installing Pi-hole with Docker Compose
&lt;/li&gt;
&lt;li&gt;Ensuring port 53 is available
&lt;/li&gt;
&lt;li&gt;Disabling the system's DNS resolver if necessary
&lt;/li&gt;
&lt;li&gt;Configuring your router to use Pi-hole
&lt;/li&gt;
&lt;li&gt;Setting up local DNS entries (if you need)&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  📦 Step 1: Install Pi-hole Using Docker Compose
&lt;/h2&gt;

&lt;p&gt;Create a &lt;code&gt;docker-compose.yml&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3"&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pihole&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pihole&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pihole/pihole:latest&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;53:53/tcp"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;53:53/udp"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8080:80"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8443:443"&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;TZ&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Asia/Dhaka"&lt;/span&gt;
      &lt;span class="na"&gt;WEBPASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;changeme"&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./etc-pihole/:/etc/pihole/"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./etc-dnsmasq.d/:/etc/dnsmasq.d/"&lt;/span&gt;
    &lt;span class="na"&gt;dns&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;127.0.0.1&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;1.1.1.1&lt;/span&gt;
    &lt;span class="na"&gt;cap_add&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;NET_ADMIN&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🚪 Step 2: Make Sure Port 53 Is Available
&lt;/h2&gt;

&lt;p&gt;Port 53 is critical for DNS. Run this to check if it's already in use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;lsof &lt;span class="nt"&gt;-i&lt;/span&gt; :53
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you see something like &lt;code&gt;systemd-resolved&lt;/code&gt; or &lt;code&gt;named&lt;/code&gt;, you need to free that port.&lt;/p&gt;




&lt;h2&gt;
  
  
  🛑 Step 3: Disable the System DNS Resolver (If Needed)
&lt;/h2&gt;

&lt;p&gt;On most Linux distros, &lt;code&gt;systemd-resolved&lt;/code&gt; binds to port 53 by default.&lt;/p&gt;

&lt;p&gt;To disable it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl disable &lt;span class="nt"&gt;--now&lt;/span&gt; systemd-resolved
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also replace the symlink for &lt;code&gt;/etc/resolv.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo rm&lt;/span&gt; /etc/resolv.conf
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"nameserver 127.0.0.1"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; /etc/resolv.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(You can also add a fallback like &lt;code&gt;8.8.8.8&lt;/code&gt; if needed.)&lt;/p&gt;




&lt;h2&gt;
  
  
  🌐 Step 4: Configure Your Router to Use Pi-hole
&lt;/h2&gt;

&lt;p&gt;To apply ad blocking to your whole network:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Log into your router admin page
&lt;/li&gt;
&lt;li&gt;Find the &lt;strong&gt;DHCP/DNS settings&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Set &lt;strong&gt;Primary DNS&lt;/strong&gt; to the IP of your Pi-hole server (e.g., &lt;code&gt;192.168.1.10&lt;/code&gt;)
&lt;/li&gt;
&lt;li&gt;Remove or override any secondary DNS that bypasses Pi-hole (like &lt;code&gt;8.8.8.8&lt;/code&gt;)
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now all devices will query Pi-hole for DNS.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧭 Step 5: Configure Local DNS (And Why)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ✅ Why Configure Local DNS?
&lt;/h3&gt;

&lt;p&gt;Setting up local DNS allows you to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Access devices by name (&lt;code&gt;nas.local&lt;/code&gt;, &lt;code&gt;printer.lan&lt;/code&gt;, etc.)&lt;/li&gt;
&lt;li&gt;Avoid typing IP addresses manually&lt;/li&gt;
&lt;li&gt;Make services on your home server feel more like the cloud&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🔧 How to Do It
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;strong&gt;Pi-hole Admin Panel → Local DNS → DNS Records&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Add entries like:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;movie.ashraful.dev → 192.168.10.10
nas.lan → 192.168.10.20
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Test from any device:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ping movie.ashraful.dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If it resolves, you're all set!&lt;/p&gt;




&lt;h2&gt;
  
  
  ✅ Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Pi-hole in Docker is clean and powerful — just make sure port 53 is free.&lt;/li&gt;
&lt;li&gt;Disable &lt;code&gt;systemd-resolved&lt;/code&gt; if it blocks port 53.&lt;/li&gt;
&lt;li&gt;Set your router’s DNS to point to Pi-hole to enable network-wide filtering.&lt;/li&gt;
&lt;li&gt;Use local DNS to make your network smarter and easier to use.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Enjoy ad-free browsing across your entire network! 🧠🛡️&lt;/p&gt;

</description>
      <category>pihole</category>
      <category>dns</category>
      <category>ubuntu</category>
      <category>linux</category>
    </item>
    <item>
      <title>Fixing DNS Resolution After Disabling systemd-resolved for Pi-hole</title>
      <dc:creator>Mohamad Ashraful Islam</dc:creator>
      <pubDate>Tue, 05 Aug 2025 04:49:15 +0000</pubDate>
      <link>https://dev.to/ashraful/fixing-dns-resolution-after-disabling-systemd-resolved-for-pi-hole-4df3</link>
      <guid>https://dev.to/ashraful/fixing-dns-resolution-after-disabling-systemd-resolved-for-pi-hole-4df3</guid>
      <description>&lt;p&gt;However, after doing this, I ran into a strange issue: some Python programs (specifically those using &lt;code&gt;dnspython&lt;/code&gt;) could no longer resolve domain names. Here's how I diagnosed and fixed the problem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Originally Posted on &lt;a href="https://blog.ashraful.dev/posts/home-server/dns-resolving-at-home-server.html" rel="noopener noreferrer"&gt;Ashraful's Blog&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🔍 The Problem
&lt;/h2&gt;

&lt;p&gt;To route DNS through Pi-hole, I disabled &lt;code&gt;systemd-resolved&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl disable &lt;span class="nt"&gt;--now&lt;/span&gt; systemd-resolved
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My home server then used Pi-hole's IP (e.g., &lt;code&gt;192.168.10.10&lt;/code&gt;) as its DNS server. Most things worked fine — &lt;code&gt;ping&lt;/code&gt;, &lt;code&gt;curl&lt;/code&gt;, and &lt;code&gt;dig&lt;/code&gt; had no issues.&lt;/p&gt;

&lt;p&gt;But some Python code using &lt;code&gt;dnspython&lt;/code&gt; threw an error like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dns.resolver.NoNameservers: All nameservers failed to answer the query
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or even:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FileNotFoundError: [Errno 2] No such file or directory: '/etc/resolv.conf'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🧠 What’s Going On?
&lt;/h2&gt;

&lt;p&gt;Many programs — including &lt;code&gt;dnspython&lt;/code&gt; — read DNS server information directly from &lt;code&gt;/etc/resolv.conf&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When &lt;code&gt;systemd-resolved&lt;/code&gt; is active, &lt;code&gt;/etc/resolv.conf&lt;/code&gt; is often a &lt;strong&gt;symlink&lt;/strong&gt; to a dynamically generated file like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/etc/resolv.conf -&amp;gt; ../run/systemd/resolve/stub-resolv.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But once &lt;code&gt;systemd-resolved&lt;/code&gt; is disabled, this symlink points to a non-existent file. Programs depending on it will fail to resolve any domains.&lt;/p&gt;




&lt;h2&gt;
  
  
  ✅ The Fix
&lt;/h2&gt;

&lt;p&gt;We need to &lt;strong&gt;replace the broken symlink&lt;/strong&gt; with a static &lt;code&gt;resolv.conf&lt;/code&gt; file that directly specifies our Pi-hole DNS.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Remove the broken symlink
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo rm&lt;/span&gt; /etc/resolv.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Create a new static file
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/resolv.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Paste your Pi-hole DNS IP (replace with your actual IP):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nameserver 192.168.10.10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Optionally, add a fallback:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nameserver 192.168.10.10
nameserver 8.8.8.8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save and close the file.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. (Optional) Prevent overwrites
&lt;/h3&gt;

&lt;p&gt;To ensure no other service modifies it (e.g., NetworkManager):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;chattr +i /etc/resolv.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To remove the protection later:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;chattr &lt;span class="nt"&gt;-i&lt;/span&gt; /etc/resolv.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🧪 Test DNS Resolution
&lt;/h2&gt;

&lt;p&gt;Using &lt;code&gt;nslookup&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nslookup google.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using Python:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dns.resolver&lt;/span&gt;
&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;resolver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;google.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If everything is working, you'll get a valid IP response.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧵 Summary
&lt;/h2&gt;

&lt;p&gt;Because I'm using &lt;strong&gt;Pi-hole as my network guardian&lt;/strong&gt;, I wanted to disable any system-level DNS services that could bypass it. Disabling &lt;code&gt;systemd-resolved&lt;/code&gt; was necessary — but it broke &lt;code&gt;/etc/resolv.conf&lt;/code&gt;, which some programs still depend on.&lt;/p&gt;

&lt;p&gt;By creating a &lt;strong&gt;manual &lt;code&gt;resolv.conf&lt;/code&gt;&lt;/strong&gt; that points to Pi-hole, I ensured full DNS functionality while keeping all traffic filtered and protected.&lt;/p&gt;




&lt;p&gt;Happy tinkering! 🛠️🧠🔒&lt;/p&gt;

</description>
      <category>pihole</category>
      <category>dns</category>
      <category>ubuntu</category>
      <category>linux</category>
    </item>
    <item>
      <title>The Untold Story of My Home Server - A Journey Through Self-Hosted Services</title>
      <dc:creator>Mohamad Ashraful Islam</dc:creator>
      <pubDate>Sun, 03 Aug 2025 05:17:15 +0000</pubDate>
      <link>https://dev.to/ashraful/the-untold-story-of-my-home-server-a-journey-through-self-hosted-services-2hj2</link>
      <guid>https://dev.to/ashraful/the-untold-story-of-my-home-server-a-journey-through-self-hosted-services-2hj2</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbdz7uowsnvrzuwakgxv4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbdz7uowsnvrzuwakgxv4.png" alt="Self Hosted Services" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why a Home Server?
&lt;/h2&gt;

&lt;p&gt;Before diving into the services, let’s talk about why I set up a home server. Cloud services like Netflix, Google Drive, or Evernote are convenient, but they come with recurring costs, privacy concerns, and dependency on someone else’s infrastructure. A home server lets me host my own media, back up my photos, manage my network, and even run custom apps—all on my terms. My setup runs on a (mini pc bought from Amazon) Ubuntu machine with Docker, making it easy to manage and scale. Here’s a look at the services that make it tick.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Immich: My Photo and Video Sanctuary
&lt;/h2&gt;

&lt;p&gt;Immich is my go-to for backing up and managing photos and videos. Think Google Photos, but self-hosted, private, and free.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Why I Chose It&lt;/strong&gt;: Immich offers automatic backups, face recognition, and a clean mobile app, all without sending my personal memories to the cloud. It’s perfect for keeping my family’s photos secure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How It Works&lt;/strong&gt;: I installed Immich via Docker, and it syncs photos and videos from my phone automatically over Wi-Fi. It organizes them by date, location, or people (using AI-based tagging), and I can access them from any device via a web interface or app.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;My Experience&lt;/strong&gt;: Immich has saved me from losing precious memories—like my kid’s first birthday video—while keeping them off Big Tech’s servers. The setup took some tweaking to get the AI features running smoothly, but it’s been worth it.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  2. Jellyfin: My Personal Netflix
&lt;/h2&gt;

&lt;p&gt;Jellyfin is the crown jewel of my home server, turning it into a media streaming powerhouse. This open-source media server lets me stream my collection of movies, TV shows, music, and even audiobooks to any device—my TV, phone, or laptop—without a subscription.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Why I Chose It&lt;/strong&gt;: Unlike Plex, which has a freemium model with paid features, Jellyfin is 100% free and open-source. It gives me full control over my media library, with no cloud dependency.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How It Works&lt;/strong&gt;: I store my media files (MP4s, MP3s, etc.) on the server’s hard drive. Jellyfin organizes them with metadata, album art, and subtitles, creating a sleek interface that rivals commercial streaming platforms. I access it via a web browser or Jellyfin’s apps on my devices.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;My Experience&lt;/strong&gt;: Setting up Jellyfin was a breeze with Docker. I love binge-watching my favorite shows on my Roku, knowing it’s all hosted at home. Plus, I can share my library with family without worrying about data leaks.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  3. Cloudreve: My Personal Cloud Storage
&lt;/h2&gt;

&lt;p&gt;Cloudreve is my Google Drive alternative, offering a self-hosted cloud storage solution for files, documents, and more.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Why I Chose It&lt;/strong&gt;: Cloudreve is lightweight, supports multiple storage backends (like local drives or S3), and has a user-friendly interface. It’s perfect for accessing files from anywhere without relying on third-party cloud providers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How It Works&lt;/strong&gt;: I set up Cloudreve to store files on my server’s hard drive. It provides a web interface and WebDAV support, so I can upload, download, or share files securely. I also use it to back up important documents.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;My Experience&lt;/strong&gt;: Cloudreve has been a game-changer for sharing large files with friends or accessing work documents remotely. The setup was straightforward, though I had to configure SSL for secure access.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  4. Pi-Hole: The Ad-Blocking Network Guardian
&lt;/h2&gt;

&lt;p&gt;Pi-Hole is my network-wide ad and tracker blocker, making my internet experience faster and cleaner.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Why I Chose It&lt;/strong&gt;: Pi-Hole blocks ads at the DNS level, meaning no pop-ups or video ads on any device connected to my network. It’s also a great way to monitor network activity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How It Works&lt;/strong&gt;: Running in a Docker container, Pi-Hole acts as my network’s DNS server. It filters out requests to known ad and tracker domains, replacing them with nothing. I pointed my router’s DNS settings to Pi-Hole, and it works seamlessly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;My Experience&lt;/strong&gt;: The difference is night and day—web pages load faster, and YouTube is ad-free on my smart TV. The dashboard showing blocked domains is oddly satisfying to check.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  5. Portainer: Keeping My Containers in Check
&lt;/h2&gt;

&lt;p&gt;Portainer is my go-to tool for managing Docker containers, making it easy to monitor and control all the services on my server.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Why I Chose It&lt;/strong&gt;: Portainer’s web-based interface simplifies Docker management, especially for someone like me who prefers a GUI over command-line tinkering.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How It Works&lt;/strong&gt;: Portainer connects to my Docker environment and lets me start, stop, or update containers, check logs, and monitor resource usage. It’s like a control panel for my server’s services.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;My Experience&lt;/strong&gt;: Portainer has saved me countless hours of debugging. I can see at a glance if Jellyfin or Immich is acting up and restart containers with a click.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  6. Nginx Proxy Manager: Simplifying Reverse Proxies
&lt;/h2&gt;

&lt;p&gt;Nginx Proxy Manager makes it easy to manage reverse proxies, allowing secure access to my services with custom domains and SSL certificates.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Why I Chose It&lt;/strong&gt;: Setting up reverse proxies manually with Nginx configs is a hassle. Nginx Proxy Manager’s graphical interface lets me configure proxies and SSL (via Let’s Encrypt) in minutes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How It Works&lt;/strong&gt;: I use it to route traffic to my services (e.g., &lt;code&gt;jellyfin.mydomain.com&lt;/code&gt; to Jellyfin’s port). It handles SSL termination, ensuring secure connections.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;My Experience&lt;/strong&gt;: This was a lifesaver for accessing my services remotely without exposing ports to the internet. The setup was intuitive, and I love the clean dashboard.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  7. Cockpit: Remote Management Made Easy
&lt;/h2&gt;

&lt;p&gt;Cockpit is my tool for remotely managing my Ubuntu server, giving me a web-based interface to monitor and tweak system settings.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Why I Chose It&lt;/strong&gt;: Cockpit is lightweight and integrates well with Ubuntu, offering a one-stop shop for system stats, updates, and user management.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How It Works&lt;/strong&gt;: Running in a Docker container, Cockpit provides a dashboard to check CPU, memory, disk usage, and logs. I can also manage services or reboot the server remotely.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;My Experience&lt;/strong&gt;: Cockpit is my safety net when I’m away from home. I once fixed a stuck service while on vacation, all from my phone’s browser.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  8. My Custom Note and Expense Tracking System: A Personal Touch
&lt;/h2&gt;

&lt;p&gt;The final piece of my home server puzzle is a note and expense tracking system I built myself, tailored to my needs.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Why I Built It&lt;/strong&gt;: I wanted a simple, private way to track daily notes and expenses without relying on apps like Notion or Mint, which store data in the cloud.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How It Works&lt;/strong&gt;: Written in [insert language, e.g., Python with Flask or Node.js], it’s a web app running in a Docker container. It has a minimalist interface for adding notes (with tags and search) and logging expenses (with categories and monthly summaries). Data is stored in a local SQLite database.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;My Experience&lt;/strong&gt;: Building this was a labor of love. It’s not perfect, but it’s mine. I use it daily to jot down ideas or track spending, and it’s satisfying to know it’s fully under my control.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Bigger Picture: Why This Matters
&lt;/h2&gt;

&lt;p&gt;Running these services on my home server has been transformative. Jellyfin and Immich keep my media and memories safe and accessible. Cloudreve and my custom app handle my files and personal data without third-party snooping. Pi-Hole, Portainer, Nginx Proxy Manager, and Cockpit keep everything running smoothly and securely. Together, they’ve saved me money, boosted my privacy, and taught me valuable skills.&lt;/p&gt;

&lt;p&gt;But beyond the tech, this setup is about empowerment. It’s about saying “no” to walled gardens and “yes” to owning your digital life. My server isn’t just a machine—it’s a statement that I can build, manage, and control my own corner of the internet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started with Your Own Home Server
&lt;/h2&gt;

&lt;p&gt;Inspired to start your own home server? Here’s a quick roadmap:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Hardware&lt;/strong&gt;: Start small with a Raspberry Pi, an old PC, or a dedicated NAS.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OS&lt;/strong&gt;: Use Ubuntu Server or a NAS-focused OS like TrueNAS for ease of use.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docker&lt;/strong&gt;: Learn Docker to simplify service deployment (Portainer makes this easier).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Services&lt;/strong&gt;: Pick one or two services (like Jellyfin or Pi-Hole) and expand from there.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Community&lt;/strong&gt;: Join communities like r/homelab or r/selfhosted on Reddit, or follow home server discussions on X for tips and inspiration.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Conclusion: The Heart of My Digital Home
&lt;/h2&gt;

&lt;p&gt;My home server is more than a collection of services; it’s a reflection of my desire for independence, creativity, and control. From streaming movies with Jellyfin to tracking expenses with my custom app, every service tells a story of problem-solving and discovery. If you’re curious about home servers, I encourage you to dive in. Start small, experiment, and join the growing community of self-hosters. What’s your home server story? Let me know in the comments or on X!&lt;/p&gt;

</description>
      <category>homeserver</category>
      <category>selfhosted</category>
      <category>docker</category>
      <category>devops</category>
    </item>
    <item>
      <title>Did you know docker-compose only takes environment variables from`.env` only?</title>
      <dc:creator>Mohamad Ashraful Islam</dc:creator>
      <pubDate>Sun, 11 Jun 2023 05:25:45 +0000</pubDate>
      <link>https://dev.to/ashraful/did-you-know-docker-compose-only-takes-environment-variables-fromenv-only-4nkf</link>
      <guid>https://dev.to/ashraful/did-you-know-docker-compose-only-takes-environment-variables-fromenv-only-4nkf</guid>
      <description>&lt;p&gt;Recently I have encountered an issue with &lt;code&gt;docker compose&lt;/code&gt;. I have separated the environment files and declared to&lt;code&gt;docker-compose.yaml&lt;/code&gt; like following,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;    &lt;span class="na"&gt;env_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.dev_env&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When I ran &lt;code&gt;docker compose up&lt;/code&gt;, it was not working on the compose itself but the container. because of environment variables. Then I found following solution for this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker compose --env-file .dev_env --env-file .prod_env up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>discuss</category>
      <category>docker</category>
      <category>compose</category>
      <category>env</category>
    </item>
    <item>
      <title>Windows is not that bad for software development</title>
      <dc:creator>Mohamad Ashraful Islam</dc:creator>
      <pubDate>Fri, 01 Jul 2022 17:34:06 +0000</pubDate>
      <link>https://dev.to/ashraful/windows-is-not-that-bad-for-software-development-2joi</link>
      <guid>https://dev.to/ashraful/windows-is-not-that-bad-for-software-development-2joi</guid>
      <description>&lt;p&gt;&lt;strong&gt;Today it's not about virtualbox/vmware. It's a new Windows 10 feature. They called it WSL(Windows Subsystem for Linux)&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Originally Posted on &lt;a href="https://blog.ashraful.dev/posts/windows-is-not-that-bad.html" rel="noopener noreferrer"&gt;HERE&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What is WSL?
&lt;/h2&gt;

&lt;p&gt;Already told &lt;strong&gt;Windows Subsystem for Linux&lt;/strong&gt;. Windows made sure to install Linux operating system inside the Windows environment without extra virtualization software(Virtual Box, VmWare, ...). So, How they did that?? That's burning question. Today I'll not go that way. But one thing I can tell that is, they slightly modified the kernel to fit with windows. Isn't it awesome?? YES! I am really exited to share about it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we are going to cover today?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Prerequisites&lt;/li&gt;
&lt;li&gt;Enable WSL.&lt;/li&gt;
&lt;li&gt;Install Ubuntu (22.04 Maybe).&lt;/li&gt;
&lt;li&gt;Set WSL Resource.&lt;/li&gt;
&lt;li&gt;Install Docker on Ubuntu.&lt;/li&gt;
&lt;li&gt;VSCode WSL development.&lt;/li&gt;
&lt;li&gt;Intellij IDE WSL development.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;You must be running Windows 10 version 2004 and higher (Build 19041 and higher) or Windows 11.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To check your Windows version and build number, select &lt;strong&gt;Windows logo key + R&lt;/strong&gt;, type &lt;strong&gt;winver&lt;/strong&gt;, select OK.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Enabling WSL
&lt;/h2&gt;

&lt;p&gt;You can enable from GUI as well as from Powershell. I always prefer the hard way(command line). So, Run the following commands on Powershell.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Enabling the Virtualization Technology
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Now you need to restart the machine.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Install WSL Update
&lt;/h3&gt;

&lt;p&gt;Downthe the update installer from &lt;a href="https://wslstorestorage.blob.core.windows.net/wslblob/wsl_update_x64.msi" rel="noopener noreferrer"&gt;here&lt;/a&gt; and Run the installer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Set WSL2 as default WSL version.
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wsl --set-default-version 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update existing distribution (if any)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wsl --set-version &amp;lt;distribution name&amp;gt; 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Install Ubuntu (22.04)
&lt;/h2&gt;

&lt;p&gt;There are several ways to install the OS in WSL. You can download from the &lt;a href="https://apps.microsoft.com/store/detail/ubuntu/9PDXGNCFSCZV" rel="noopener noreferrer"&gt;Windows Store&lt;/a&gt;. This is the hassel free process. Once installed you will be able to login using your given password. &lt;strong&gt;Don't afraid, there is no UI. Only command line like Ubuntu Server :)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now open Powershell again,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wsl -l -v 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will get something like following,&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvjgz0fcyr49rez00mqj2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvjgz0fcyr49rez00mqj2.png" alt="WSL Version" width="597" height="183"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Set WSL Resource
&lt;/h2&gt;

&lt;p&gt;Yes. You can still have chance to modify your resource allocation. Otherwise your windows will decide to do so. The process is pretty simple. You need to create a file to a specific location.&lt;br&gt;&lt;br&gt;
Open the file,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;notepad "$env:USERPROFILE\.wslconfig"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add these lines to the editor and save it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[wsl2]
memory=4GB  
processors=4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;You can read the &lt;a href="https://docs.microsoft.com/en-us/windows/wsl/wsl-config" rel="noopener noreferrer"&gt;microsoft's document&lt;/a&gt; for more configuration&lt;/em&gt;.&lt;br&gt;&lt;br&gt;
Now login to the ubuntu again, with Powershell/CMD whatever shell you have.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wsl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h2&gt;
  
  
  Install Docker on WSL(Ubuntu)
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;It's not mandatory. So, don't push yourself into Docker if your are not a Docker guy.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Installing Docker is same guide as ubuntu. You can follow the official &lt;a href="https://docs.docker.com/engine/install/ubuntu/" rel="noopener noreferrer"&gt;Docker docs&lt;/a&gt; OR blindly follow me. Run the following commands on the Ubuntu's terminal &lt;strong&gt;not Powershell&lt;/strong&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Update the apt package index and install packages to allow apt to use a repository over HTTPS:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo apt update
$ sudo apt install \
   ca-certificates \
   curl \
   gnupg \
   lsb-release
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Add Docker’s official GPG key:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /etc/apt/keyrings
&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://download.docker.com/linux/ubuntu/gpg | &lt;span class="nb"&gt;sudo &lt;/span&gt;gpg &lt;span class="nt"&gt;--dearmor&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /etc/apt/keyrings/docker.gpg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Use the following command to set up the repository
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"deb [arch=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;dpkg &lt;span class="nt"&gt;--print-architecture&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
  &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;lsb_release &lt;span class="nt"&gt;-cs&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; stable"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; /etc/apt/sources.list.d/docker.list &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Install Docker Engine
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;docker-ce docker-ce-cli containerd.io docker-compose-plugin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Start the docker and check
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo service docker start
$ sudo service docker status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Test your docker installation
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo docker run hello-world
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe8lzhugmaaqs740uwv7e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe8lzhugmaaqs740uwv7e.png" alt="WSL Docker Hello" width="800" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So, Docker is working.&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Incase any Network issue from WSL
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ echo -e "[network]\ngenerateResolvConf = false" | sudo tee -a /etc/wsl.conf
$ sudo unlink /etc/resolv.conf
$ echo nameserver 1.1.1.1 | sudo tee /etc/resolv.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  IPTables related issues
&lt;/h3&gt;

&lt;p&gt;I found some people have iptables related errors. So I recommend the following command and &lt;strong&gt;choose legacy&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;update-alternatives &lt;span class="nt"&gt;--config&lt;/span&gt; iptables
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Setting up VSCode
&lt;/h2&gt;

&lt;p&gt;Actually there is nothing to setup. Trust me and do the following things,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You just need to install an extension. You can get it &lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-wsl" rel="noopener noreferrer"&gt;from here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Create a project on WSL(ubuntu)&lt;/li&gt;
&lt;li&gt;On the bottom left corner you will see remote development button. &lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;New WSL Window&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Open the project from WSL(Ubuntu)
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmnfxwlvkwjngq2hxgkaa.png" alt="VSCode" width="800" height="501"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Now it's all yours.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up Intellij IDE (Pycharm in my case.)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Using Intellij IDE (Pycharm) codes stays on the windows machine.&lt;/li&gt;
&lt;li&gt;Interpreter(Environment) will be just shared from WSL.
The step by step guide is &lt;a href="https://www.jetbrains.com/help/pycharm/using-wsl-as-a-remote-interpreter.html#configure-wsl" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I've talked too much today. But it made me to do so. But, Why I am writing this post?&lt;br&gt;&lt;br&gt;
&lt;strong&gt;In short&lt;/strong&gt;, After 7-8 years I have been trying windows 10. I am so pleased to see the WSL and how it works. So, I thought people may like it too. Thank you.&lt;/p&gt;

</description>
      <category>windows</category>
      <category>development</category>
      <category>wsl</category>
      <category>linux</category>
    </item>
  </channel>
</rss>
