<?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: Sospeter Mong'are</title>
    <description>The latest articles on DEV Community by Sospeter Mong'are (@msnmongare).</description>
    <link>https://dev.to/msnmongare</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%2F117091%2F89122cee-2645-481e-b979-f96819dc9d1b.jpeg</url>
      <title>DEV Community: Sospeter Mong'are</title>
      <link>https://dev.to/msnmongare</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/msnmongare"/>
    <language>en</language>
    <item>
      <title>RAG Is Not an AI Agent</title>
      <dc:creator>Sospeter Mong'are</dc:creator>
      <pubDate>Fri, 10 Jul 2026 07:11:23 +0000</pubDate>
      <link>https://dev.to/msnmongare/rag-is-not-an-ai-agent-b7a</link>
      <guid>https://dev.to/msnmongare/rag-is-not-an-ai-agent-b7a</guid>
      <description>&lt;p&gt;One of the biggest misconceptions in AI today is treating &lt;strong&gt;Retrieval-Augmented Generation (RAG)&lt;/strong&gt; and &lt;strong&gt;AI agents&lt;/strong&gt; as the same thing.&lt;/p&gt;

&lt;p&gt;They're not.&lt;/p&gt;

&lt;p&gt;RAG improves an LLM's ability to answer questions using external knowledge. AI agents go a step further by reasoning, planning, making decisions, and taking actions.&lt;/p&gt;

&lt;p&gt;Understanding the difference will help you design better AI systems and avoid using the wrong architecture for the job.&lt;/p&gt;




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

&lt;p&gt;Retrieval-Augmented Generation (RAG) is an architecture that gives an LLM access to information outside its training data.&lt;/p&gt;

&lt;p&gt;Instead of relying solely on what the model learned during training, it retrieves relevant documents from a knowledge base and includes them in the prompt before generating a response.&lt;/p&gt;

&lt;p&gt;Think of RAG as &lt;strong&gt;"open-book AI."&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  RAG Architecture
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;          User Question
                 │
                 ▼
        Embed the Question
                 │
                 ▼
         Vector Database
                 │
       Retrieve Documents
                 │
                 ▼
      Prompt + Retrieved Context
                 │
                 ▼
                LLM
                 │
                 ▼
             Final Answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The LLM does &lt;strong&gt;not&lt;/strong&gt; search the internet by itself or decide what action to take. It simply answers using the retrieved context.&lt;/p&gt;




&lt;h2&gt;
  
  
  What RAG Does Well
&lt;/h2&gt;

&lt;p&gt;RAG is excellent when your AI needs accurate, up-to-date, or organization-specific information.&lt;/p&gt;

&lt;p&gt;Common use cases include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Internal company knowledge bases&lt;/li&gt;
&lt;li&gt;Customer support chatbots&lt;/li&gt;
&lt;li&gt;HR policy assistants&lt;/li&gt;
&lt;li&gt;Product documentation&lt;/li&gt;
&lt;li&gt;Legal document search&lt;/li&gt;
&lt;li&gt;Medical reference systems&lt;/li&gt;
&lt;li&gt;Financial knowledge assistants&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of retraining an LLM every time information changes, you simply update the knowledge base.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where RAG Ends
&lt;/h2&gt;

&lt;p&gt;This is where many people get confused.&lt;/p&gt;

&lt;p&gt;A RAG system can retrieve information and generate an answer.&lt;/p&gt;

&lt;p&gt;It &lt;strong&gt;cannot inherently&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Decide which tool to use&lt;/li&gt;
&lt;li&gt;Execute code&lt;/li&gt;
&lt;li&gt;Send emails&lt;/li&gt;
&lt;li&gt;Book meetings&lt;/li&gt;
&lt;li&gt;Query multiple systems intelligently&lt;/li&gt;
&lt;li&gt;Break large tasks into smaller ones&lt;/li&gt;
&lt;li&gt;Plan multiple steps&lt;/li&gt;
&lt;li&gt;Retry after failures&lt;/li&gt;
&lt;li&gt;Decide whether another search is needed&lt;/li&gt;
&lt;/ul&gt;

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

&lt;blockquote&gt;
&lt;p&gt;"Find all unpaid invoices and email each customer."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A RAG system cannot complete this task.&lt;/p&gt;

&lt;p&gt;It might explain &lt;em&gt;how&lt;/em&gt; to do it, but it won't actually perform the work.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where AI Agents Begin
&lt;/h2&gt;

&lt;p&gt;AI agents are built for &lt;strong&gt;decision-making and action&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Instead of following one fixed pipeline, an agent reasons about the task before deciding what to do next.&lt;/p&gt;

&lt;p&gt;A typical agent can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Understand the objective&lt;/li&gt;
&lt;li&gt;Create a plan&lt;/li&gt;
&lt;li&gt;Choose the right tool&lt;/li&gt;
&lt;li&gt;Execute the tool&lt;/li&gt;
&lt;li&gt;Evaluate the result&lt;/li&gt;
&lt;li&gt;Try another approach if needed&lt;/li&gt;
&lt;li&gt;Continue until the goal is achieved&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  AI Agent Architecture
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;              User Request
                    │
                    ▼
                 Planner
                    │
      ┌─────────────┼─────────────┐
      ▼             ▼             ▼
 Search Web     Query Database   Run Code
      │             │             │
      └─────────────┼─────────────┘
                    ▼
              Evaluate Result
                    │
          Need Another Step?
            Yes           No
             │             │
             ▼             ▼
       Choose Next Tool   Final Answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the difference.&lt;/p&gt;

&lt;p&gt;The workflow isn't fixed.&lt;/p&gt;

&lt;p&gt;The agent decides what to do while solving the problem.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Key Difference
&lt;/h2&gt;

&lt;p&gt;A RAG pipeline follows a predefined sequence:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Retrieve → Generate → Respond
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An agent follows a reasoning loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Think → Plan → Choose Tool → Execute → Observe → Repeat
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One retrieves knowledge.&lt;/p&gt;

&lt;p&gt;The other solves problems.&lt;/p&gt;




&lt;h2&gt;
  
  
  Can an AI Agent Use RAG?
&lt;/h2&gt;

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

&lt;p&gt;In fact, many production AI agents use RAG as &lt;strong&gt;one of their tools&lt;/strong&gt;.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                 AI Agent
                     │
     ┌───────────────┼────────────────┐
     ▼               ▼                ▼
     RAG         SQL Database      Calendar API
     │               │                │
     └───────────────┼────────────────┘
                     ▼
              Final Response
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, RAG is simply one capability among many.&lt;/p&gt;

&lt;p&gt;The agent decides &lt;strong&gt;when&lt;/strong&gt; to retrieve documents and &lt;strong&gt;when&lt;/strong&gt; to use other tools.&lt;/p&gt;




&lt;h2&gt;
  
  
  When Should You Use RAG?
&lt;/h2&gt;

&lt;p&gt;Choose RAG when your goal is to answer questions using trusted knowledge.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Company documentation chatbot&lt;/li&gt;
&lt;li&gt;University handbook assistant&lt;/li&gt;
&lt;li&gt;Product manuals&lt;/li&gt;
&lt;li&gt;Insurance policy lookup&lt;/li&gt;
&lt;li&gt;Research assistant&lt;/li&gt;
&lt;li&gt;FAQ systems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If users mainly ask questions, RAG is often enough.&lt;/p&gt;




&lt;h2&gt;
  
  
  When Should You Use an AI Agent?
&lt;/h2&gt;

&lt;p&gt;Choose an AI agent when the system needs to make decisions or perform actions.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Schedule meetings&lt;/li&gt;
&lt;li&gt;Generate reports from multiple systems&lt;/li&gt;
&lt;li&gt;Process invoices&lt;/li&gt;
&lt;li&gt;Customer service workflows&lt;/li&gt;
&lt;li&gt;Software engineering assistants&lt;/li&gt;
&lt;li&gt;DevOps automation&lt;/li&gt;
&lt;li&gt;Data analysis&lt;/li&gt;
&lt;li&gt;Multi-step business processes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the task involves reasoning, planning, or interacting with multiple tools, an agent is the better fit.&lt;/p&gt;




&lt;h2&gt;
  
  
  RAG vs AI Agent at a Glance
&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;RAG&lt;/th&gt;
&lt;th&gt;AI Agent&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Retrieves knowledge&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Answers questions&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Uses external tools&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Makes decisions&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Plans multiple steps&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Executes actions&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Adapts workflow dynamically&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Retries after failures&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




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

&lt;p&gt;RAG and AI agents aren't competing technologies.&lt;/p&gt;

&lt;p&gt;They're complementary.&lt;/p&gt;

&lt;p&gt;RAG gives an AI access to knowledge.&lt;/p&gt;

&lt;p&gt;AI agents give an AI the ability to reason, decide, and act.&lt;/p&gt;

&lt;p&gt;A useful way to remember it is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RAG = LLM + Your Data

AI Agent = LLM + Memory + Planning + Tools + State

Production AI = Agent + RAG + Reliable Infrastructure
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The most capable AI systems today don't choose between RAG and agents. They combine both. The agent decides &lt;em&gt;what&lt;/em&gt; needs to be done, while RAG ensures it has the right information to do it well.&lt;/p&gt;

</description>
      <category>rag</category>
      <category>ai</category>
      <category>beginners</category>
      <category>python</category>
    </item>
    <item>
      <title>LangChain &amp; LangGraph Concepts You Should Know</title>
      <dc:creator>Sospeter Mong'are</dc:creator>
      <pubDate>Fri, 10 Jul 2026 05:58:30 +0000</pubDate>
      <link>https://dev.to/msnmongare/langchain-langgraph-concepts-you-should-know-1la5</link>
      <guid>https://dev.to/msnmongare/langchain-langgraph-concepts-you-should-know-1la5</guid>
      <description>&lt;p&gt;Here are five foundational &lt;strong&gt;LangChain&lt;/strong&gt; and &lt;strong&gt;LangGraph&lt;/strong&gt; concepts every AI engineer should understand.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Chains (The AI Workflow)
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;chain&lt;/strong&gt; is a sequence of steps where the output of one step becomes the input of the next.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&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;User Question
      ↓
Retrieve Documents
      ↓
LLM Generates Answer
      ↓
Format Response
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Think of it as a pipeline for AI tasks.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Tools (Giving AI Superpowers)
&lt;/h3&gt;

&lt;p&gt;LLMs only know what was in their training data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tools&lt;/strong&gt; let them interact with the outside world.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Search the web&lt;/li&gt;
&lt;li&gt;Query SQL databases&lt;/li&gt;
&lt;li&gt;Call REST APIs&lt;/li&gt;
&lt;li&gt;Execute Python code&lt;/li&gt;
&lt;li&gt;Read PDFs&lt;/li&gt;
&lt;li&gt;Send emails&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of only generating text, the AI can now &lt;em&gt;do things&lt;/em&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. Memory (Remembering Conversations)
&lt;/h3&gt;

&lt;p&gt;Memory allows an AI to remember information across interactions.&lt;/p&gt;

&lt;p&gt;Without memory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User: My name is Sam.
...
User: What's my name?

AI: I don't know.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With memory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AI: Your name is Sam.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Memory can be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Short-term (current conversation)&lt;/li&gt;
&lt;li&gt;Long-term (saved facts)&lt;/li&gt;
&lt;li&gt;Semantic memory (knowledge)&lt;/li&gt;
&lt;li&gt;Episodic memory (past interactions)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  4. Agents (Reason + Choose Tools)
&lt;/h3&gt;

&lt;p&gt;An &lt;strong&gt;agent&lt;/strong&gt; doesn't follow a fixed workflow.&lt;/p&gt;

&lt;p&gt;Instead, it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Understands the goal&lt;/li&gt;
&lt;li&gt;Decides what to do&lt;/li&gt;
&lt;li&gt;Chooses the right tool&lt;/li&gt;
&lt;li&gt;Executes it&lt;/li&gt;
&lt;li&gt;Evaluates the result&lt;/li&gt;
&lt;li&gt;Repeats until the task is complete&lt;/li&gt;
&lt;/ol&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;User:
"Find the latest exchange rate and calculate how much 250 USD is in KES."

Agent:
→ Search exchange rate
→ Use calculator
→ Return final answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The workflow is dynamic rather than predetermined.&lt;/p&gt;




&lt;h3&gt;
  
  
  5. Graphs (LangGraph's Superpower)
&lt;/h3&gt;

&lt;p&gt;Traditional chains are linear.&lt;/p&gt;

&lt;p&gt;LangGraph introduces &lt;strong&gt;graphs&lt;/strong&gt;, where execution can branch, loop, pause, or resume.&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;          Start
             │
             ▼
      Understand Task
        ┌────┴────┐
        ▼         ▼
   Search Web   Query Database
        │         │
        └────┬────┘
             ▼
      Evaluate Results
        ┌────┴────┐
     Good?      No
       │         │
       ▼         │
    Final Answer │
                 │
                 ▼
          Try Another Tool
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes LangGraph ideal for building autonomous AI agents that can recover from errors, make decisions, and manage complex workflows.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Simple Way to Remember
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;LangChain&lt;/strong&gt; = LLM + tools + workflows&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agents&lt;/strong&gt; = LangChain + reasoning + tool selection&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LangGraph&lt;/strong&gt; = Agents + state + loops + branching + durable execution&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're learning modern AI engineering, mastering these five concepts will give you a strong foundation for building production-ready AI applications.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>beginners</category>
      <category>langchain</category>
      <category>python</category>
    </item>
    <item>
      <title>Understanding Integration Runtimes in Azure Data Factory (ADF)</title>
      <dc:creator>Sospeter Mong'are</dc:creator>
      <pubDate>Thu, 02 Jul 2026 12:54:49 +0000</pubDate>
      <link>https://dev.to/msnmongare/understanding-integration-runtimes-in-azure-data-factory-adf-2pc6</link>
      <guid>https://dev.to/msnmongare/understanding-integration-runtimes-in-azure-data-factory-adf-2pc6</guid>
      <description>&lt;p&gt;When people begin learning Azure Data Factory (ADF), one concept that often causes confusion is the &lt;strong&gt;Integration Runtime (IR)&lt;/strong&gt;. Pipelines, datasets, and linked services are fairly intuitive, but many beginners wonder:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"If Azure Data Factory orchestrates everything, what exactly is an Integration Runtime?"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The simplest answer is this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;An Integration Runtime is the compute infrastructure that Azure Data Factory uses to move data, transform data, and execute activities.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Think of Azure Data Factory as the brain that plans the work, while the Integration Runtime is the muscle that carries it out.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Do We Need an Integration Runtime?
&lt;/h2&gt;

&lt;p&gt;Imagine you're a logistics manager.&lt;/p&gt;

&lt;p&gt;You create a schedule for transporting goods from Nairobi to Mombasa. You know what should be transported, when it should leave, and where it should arrive.&lt;/p&gt;

&lt;p&gt;However, without a truck, nothing actually moves.&lt;/p&gt;

&lt;p&gt;Azure Data Factory works the same way.&lt;/p&gt;

&lt;p&gt;A pipeline defines &lt;em&gt;what&lt;/em&gt; should happen, but the Integration Runtime is responsible for actually performing the work.&lt;/p&gt;

&lt;p&gt;It is responsible for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Moving data between different data sources&lt;/li&gt;
&lt;li&gt;Running data transformation jobs&lt;/li&gt;
&lt;li&gt;Connecting securely to cloud and on-premises systems&lt;/li&gt;
&lt;li&gt;Executing activities within your pipeline&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without an Integration Runtime, your pipeline has instructions but no execution engine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Types of Integration Runtime
&lt;/h2&gt;

&lt;p&gt;Azure Data Factory provides three types of Integration Runtime, each designed for different scenarios.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Azure Integration Runtime
&lt;/h2&gt;

&lt;p&gt;This is the default Integration Runtime provided and managed by Microsoft.&lt;/p&gt;

&lt;p&gt;You don't install or maintain any servers. Azure automatically provisions and scales the compute resources needed to execute your workloads.&lt;/p&gt;

&lt;p&gt;It is ideal when your data resides entirely in the cloud.&lt;/p&gt;

&lt;p&gt;Common use cases include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Copying data from Azure SQL Database to Azure Data Lake&lt;/li&gt;
&lt;li&gt;Moving data between cloud storage accounts&lt;/li&gt;
&lt;li&gt;Running Mapping Data Flows&lt;/li&gt;
&lt;li&gt;Executing cloud-native activities&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A typical architecture 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;Azure SQL Database
        │
        ▼
Azure Integration Runtime
        │
        ▼
Azure Data Lake Storage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Fully managed by Microsoft&lt;/li&gt;
&lt;li&gt;Automatically scales&lt;/li&gt;
&lt;li&gt;No infrastructure to maintain&lt;/li&gt;
&lt;li&gt;Highly available&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For most cloud-to-cloud data movement, Azure Integration Runtime is the recommended choice.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Self-hosted Integration Runtime (SHIR)
&lt;/h2&gt;

&lt;p&gt;What happens if your data is not in Azure?&lt;/p&gt;

&lt;p&gt;Suppose your organization has an on-premises SQL Server inside a corporate network protected by a firewall.&lt;/p&gt;

&lt;p&gt;Azure Data Factory cannot directly access it.&lt;/p&gt;

&lt;p&gt;This is where the Self-hosted Integration Runtime comes in.&lt;/p&gt;

&lt;p&gt;The Self-hosted IR is software that you install on a Windows machine, virtual machine, or server within your network.&lt;/p&gt;

&lt;p&gt;It acts as a secure bridge between Azure and your private environment.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;On-Prem SQL Server
        │
        ▼
Self-hosted Integration Runtime
        │
        ▼
Azure Blob Storage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because the runtime resides inside your network, it can securely communicate with your local databases while also communicating with Azure Data Factory.&lt;/p&gt;

&lt;h3&gt;
  
  
  Common use cases
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;SQL Server&lt;/li&gt;
&lt;li&gt;Oracle Database&lt;/li&gt;
&lt;li&gt;MySQL&lt;/li&gt;
&lt;li&gt;PostgreSQL&lt;/li&gt;
&lt;li&gt;SAP&lt;/li&gt;
&lt;li&gt;Local file servers&lt;/li&gt;
&lt;li&gt;Private APIs&lt;/li&gt;
&lt;li&gt;Any data source behind a firewall&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is one of the most commonly used Integration Runtimes in enterprise environments.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Azure SSIS Integration Runtime
&lt;/h2&gt;

&lt;p&gt;Many organizations have existing ETL processes built using SQL Server Integration Services (SSIS).&lt;/p&gt;

&lt;p&gt;Rather than rebuilding those packages from scratch, Azure allows you to run them in the cloud using Azure SSIS Integration Runtime.&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;SSIS Package
      │
      ▼
Azure SSIS Integration Runtime
      │
      ▼
Azure SQL Database
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This option is mainly used during cloud migration projects where businesses want to continue using their existing SSIS investments.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Real-World Example
&lt;/h2&gt;

&lt;p&gt;Imagine you work for a bank.&lt;/p&gt;

&lt;p&gt;Every night, customer transactions stored in an on-premises SQL Server need to be copied into Azure Data Lake for reporting and analytics.&lt;/p&gt;

&lt;p&gt;The architecture would look something 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;ADF Pipeline
      │
      ▼
Self-hosted Integration Runtime
      │
      ▼
On-Prem SQL Server
      │
      ▼
Azure Data Lake Storage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's what happens:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The pipeline starts according to a schedule.&lt;/li&gt;
&lt;li&gt;Azure Data Factory sends the job to the Self-hosted Integration Runtime.&lt;/li&gt;
&lt;li&gt;The Self-hosted IR connects to the SQL Server.&lt;/li&gt;
&lt;li&gt;It reads the required data.&lt;/li&gt;
&lt;li&gt;It securely transfers the data into Azure Data Lake.&lt;/li&gt;
&lt;li&gt;The pipeline completes successfully.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Notice that Azure Data Factory never directly connects to the SQL Server. The Self-hosted Integration Runtime performs that task.&lt;/p&gt;




&lt;h2&gt;
  
  
  Which Integration Runtime Should You Use?
&lt;/h2&gt;

&lt;p&gt;The choice depends on where your data resides and what type of workload you're running.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Recommended Integration Runtime&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Azure SQL Database to Azure Blob Storage&lt;/td&gt;
&lt;td&gt;Azure Integration Runtime&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Azure SQL Database to Azure Data Lake&lt;/td&gt;
&lt;td&gt;Azure Integration Runtime&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SQL Server on-premises to Azure&lt;/td&gt;
&lt;td&gt;Self-hosted Integration Runtime&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Oracle behind a firewall&lt;/td&gt;
&lt;td&gt;Self-hosted Integration Runtime&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Local files to Azure Storage&lt;/td&gt;
&lt;td&gt;Self-hosted Integration Runtime&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Running Mapping Data Flows&lt;/td&gt;
&lt;td&gt;Azure Integration Runtime&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Running existing SSIS packages&lt;/td&gt;
&lt;td&gt;Azure SSIS Integration Runtime&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Understanding the Difference Between Pipelines and Integration Runtime
&lt;/h2&gt;

&lt;p&gt;One common misconception among beginners is assuming that pipelines execute the work.&lt;/p&gt;

&lt;p&gt;They don't.&lt;/p&gt;

&lt;p&gt;A pipeline is simply an orchestration layer.&lt;/p&gt;

&lt;p&gt;Think of the relationship this way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pipeline&lt;/strong&gt; decides &lt;em&gt;what&lt;/em&gt; should happen.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Activity&lt;/strong&gt; defines the specific task.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Linked Service&lt;/strong&gt; stores the connection information.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dataset&lt;/strong&gt; represents the data being used.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integration Runtime&lt;/strong&gt; performs the actual work.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without an Integration Runtime, the pipeline has instructions but no execution engine.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Simple Analogy
&lt;/h2&gt;

&lt;p&gt;Imagine you're running a courier company.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The customer places an order.&lt;/li&gt;
&lt;li&gt;The operations manager plans the delivery.&lt;/li&gt;
&lt;li&gt;The address tells the driver where to collect and deliver the package.&lt;/li&gt;
&lt;li&gt;The package is the data.&lt;/li&gt;
&lt;li&gt;The truck transports the package.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In Azure Data Factory:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Customer order = Business requirement&lt;/li&gt;
&lt;li&gt;Pipeline = Delivery plan&lt;/li&gt;
&lt;li&gt;Activity = Delivery task&lt;/li&gt;
&lt;li&gt;Linked Service = Pickup and destination addresses&lt;/li&gt;
&lt;li&gt;Dataset = The package&lt;/li&gt;
&lt;li&gt;Integration Runtime = The truck&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without the truck, nothing gets delivered.&lt;/p&gt;




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

&lt;p&gt;Integration Runtime is one of the most important concepts in Azure Data Factory because it is responsible for executing your data integration workloads.&lt;/p&gt;

&lt;p&gt;Choosing the correct Integration Runtime depends on your environment:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;Azure Integration Runtime&lt;/strong&gt; for cloud-to-cloud data movement and transformations.&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;Self-hosted Integration Runtime&lt;/strong&gt; when accessing on-premises or private network resources.&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;Azure SSIS Integration Runtime&lt;/strong&gt; when migrating or running existing SSIS packages in Azure.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you understand the role of the Integration Runtime, Azure Data Factory becomes much easier to visualize. Instead of thinking of it as just another Azure service, you'll see it as a complete orchestration platform where pipelines coordinate the work, and Integration Runtimes provide the execution power that makes everything happen.&lt;/p&gt;

</description>
      <category>azure</category>
      <category>azuredatafactory</category>
      <category>beginners</category>
      <category>programming</category>
    </item>
    <item>
      <title>How to Install Apache Airflow 3.1.3 on WSL (Windows Subsystem for Linux)</title>
      <dc:creator>Sospeter Mong'are</dc:creator>
      <pubDate>Wed, 01 Jul 2026 07:46:51 +0000</pubDate>
      <link>https://dev.to/msnmongare/how-to-install-apache-airflow-313-on-wsl-windows-subsystem-for-linux-1kk5</link>
      <guid>https://dev.to/msnmongare/how-to-install-apache-airflow-313-on-wsl-windows-subsystem-for-linux-1kk5</guid>
      <description>&lt;h2&gt;
  
  
  What is Apache Airflow?
&lt;/h2&gt;

&lt;p&gt;Apache Airflow is an open-source platform used to programmatically author, schedule, and monitor workflows. It's widely used in data engineering, MLOps, and backend automation pipelines. If you're building ETL pipelines, running scheduled tasks, or orchestrating complex workflows, Airflow is a tool you'll likely encounter.&lt;/p&gt;

&lt;p&gt;Airflow 3 is a major upgrade from Airflow 2 - it introduces a new React-based UI, a dedicated API server (replacing the old webserver), improved task isolation via the Task SDK, and better scalability overall.&lt;/p&gt;




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

&lt;p&gt;Before we begin, make sure you have the following in place:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Windows Subsystem for Linux (WSL2) installed
&lt;/h3&gt;

&lt;p&gt;You need WSL 2 with &lt;strong&gt;Ubuntu 24.04 (Noble)&lt;/strong&gt; installed on your Windows machine. If you haven't set it up yet, open PowerShell as Administrator and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;wsl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--install&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restart your machine when prompted, then launch &lt;strong&gt;Ubuntu&lt;/strong&gt; from the Start menu.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; Always work inside the Linux home directory (&lt;code&gt;~&lt;/code&gt;), not a Windows-mounted path like &lt;code&gt;/mnt/c/...&lt;/code&gt;. Performance and file permission issues can occur on mounted drives.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  2. MobiXterm or Windows Terminal (optional but recommended)
&lt;/h3&gt;

&lt;p&gt;Either gives you a better terminal experience for working inside WSL.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Basic familiarity with the Linux terminal
&lt;/h3&gt;

&lt;p&gt;You don't need to be a Linux expert, but knowing how to navigate directories (&lt;code&gt;cd&lt;/code&gt;, &lt;code&gt;ls&lt;/code&gt;) and run commands will help.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: Update Your System
&lt;/h2&gt;

&lt;p&gt;Open your WSL terminal and run:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;This ensures your package lists and installed packages are up to date.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: Install Python 3 and Check the Version
&lt;/h2&gt;

&lt;p&gt;Ubuntu 24.04 ships with Python 3.12 by default. Verify it's available:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;You should see something like &lt;code&gt;Python 3.12.x&lt;/code&gt;. Airflow 3.1.3 supports Python 3.10, 3.11, and 3.12 - so you're good.&lt;/p&gt;

&lt;p&gt;If Python isn't installed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;python3 python3-dev python3-pip python3-venv &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 3: Set Your AIRFLOW_HOME (Important - Do This First)
&lt;/h2&gt;

&lt;p&gt;Airflow needs a home directory to store its configuration, logs, and database. Set this &lt;strong&gt;before&lt;/strong&gt; installing Airflow so it knows where to store everything:&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;export &lt;/span&gt;&lt;span class="nv"&gt;AIRFLOW_HOME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;~/airflow-3.1.3/airflow_home
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To make this permanent so it survives terminal restarts:&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;echo&lt;/span&gt; &lt;span class="s1"&gt;'export AIRFLOW_HOME=~/airflow-3.1.3/airflow_home'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; ~/.bashrc
&lt;span class="nb"&gt;source&lt;/span&gt; ~/.bashrc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why this matters:&lt;/strong&gt; If you skip this step, Airflow defaults to &lt;code&gt;~/airflow&lt;/code&gt; and may pick up config files from a previous Airflow installation, causing unexpected warnings or errors.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 4: Create a Project Directory and Virtual Environment
&lt;/h2&gt;

&lt;p&gt;Create a dedicated folder for your Airflow 3.1.3 installation:&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;mkdir&lt;/span&gt; ~/airflow-3.1.3
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/airflow-3.1.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a virtual environment inside it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; venv &lt;span class="nb"&gt;env&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Activate the virtual environment:&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;source env&lt;/span&gt;/bin/activate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your terminal prompt should now show &lt;code&gt;(env)&lt;/code&gt; at the beginning:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;(env) user@DESKTOP-XXXX:~/airflow-3.1.3$&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 5: Upgrade pip
&lt;/h2&gt;

&lt;p&gt;Before installing Airflow, upgrade pip to the latest version:&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; &lt;span class="nt"&gt;--upgrade&lt;/span&gt; pip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 6: Install Apache Airflow 3.1.3 with Constraints
&lt;/h2&gt;

&lt;p&gt;Install Airflow using the official constraints file. This is &lt;strong&gt;critical&lt;/strong&gt; - Airflow has many dependencies, and the constraints file ensures compatible versions are installed together without conflicts:&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; &lt;span class="s2"&gt;"apache-airflow==3.1.3"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--constraint&lt;/span&gt; &lt;span class="s2"&gt;"https://raw.githubusercontent.com/apache/airflow/constraints-3.1.3/constraints-3.12.txt"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This may take several minutes. Once complete, verify the installation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;airflow version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 7: Install the FAB Auth Manager Provider
&lt;/h2&gt;

&lt;p&gt;In Airflow 3, user authentication is handled by a separate provider package. Install it now:&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;apache-airflow-providers-fab &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--constraint&lt;/span&gt; &lt;span class="s2"&gt;"https://raw.githubusercontent.com/apache/airflow/constraints-3.1.3/constraints-3.12.txt"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify it's registered:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;airflow providers list | &lt;span class="nb"&gt;grep &lt;/span&gt;fab
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apache-airflow-providers-fab  |  Flask App Builder ...  |  3.x.x
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 7b: Set FAB as the Auth Manager (Critical)
&lt;/h2&gt;

&lt;p&gt;This is the most important step that is easy to miss. Airflow 3 ships with a new lightweight &lt;code&gt;SimpleAuthManager&lt;/code&gt; as the default. While it works for single-user local setups, it does &lt;strong&gt;not&lt;/strong&gt; expose the &lt;code&gt;airflow users&lt;/code&gt; CLI command - meaning you won't be able to create admin users from the terminal.&lt;/p&gt;

&lt;p&gt;You must explicitly switch to the FAB auth manager.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check what auth manager is currently set:&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;airflow config get-value core auth_manager
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If it returns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;airflow.api_fastapi.auth.managers.simple.simple_auth_manager.SimpleAuthManager
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You are on the wrong auth manager. Switch it by opening your &lt;code&gt;airflow.cfg&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;nano ~/airflow-3.1.3/airflow_home/airflow.cfg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Find the &lt;code&gt;[core]&lt;/code&gt; section and change:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[core]&lt;/span&gt;
&lt;span class="py"&gt;auth_manager&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;airflow.api_fastapi.auth.managers.simple.simple_auth_manager.SimpleAuthManager&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[core]&lt;/span&gt;
&lt;span class="py"&gt;auth_manager&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;airflow.providers.fab.auth_manager.fab_auth_manager.FabAuthManager&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save and exit (&lt;code&gt;Ctrl+O&lt;/code&gt;, &lt;code&gt;Enter&lt;/code&gt;, &lt;code&gt;Ctrl+X&lt;/code&gt;). Verify the change:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;airflow config get-value core auth_manager
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Should now return:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;airflow.providers.fab.auth_manager.fab_auth_manager.FabAuthManager
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;SimpleAuthManager vs FabAuthManager:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;SimpleAuthManager&lt;/code&gt; - Airflow 3's new default. Lightweight, no CLI user management, suited for single-user or automated setups&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;FabAuthManager&lt;/code&gt; - Full-featured auth manager from Flask AppBuilder. Supports CLI user creation, roles, permissions, and OAuth. Required for proper multi-user setups and for the &lt;code&gt;airflow users&lt;/code&gt; command to work&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; You may see a &lt;code&gt;RemovedInAirflow4Warning&lt;/code&gt; about &lt;code&gt;airflow.security.permissions&lt;/code&gt; being deprecated. This is harmless in 3.1.3 - it's just a heads-up that some internal FAB APIs will change in Airflow 4.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 8: Initialize the Airflow Database
&lt;/h2&gt;

&lt;p&gt;Airflow 3 uses &lt;code&gt;db migrate&lt;/code&gt; instead of the old &lt;code&gt;db init&lt;/code&gt; command. This creates the metadata database and applies all schema migrations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;airflow db migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 9: Create an Admin User
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;airflow &lt;span class="nb"&gt;users &lt;/span&gt;create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--username&lt;/span&gt; admin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--firstname&lt;/span&gt; YourFirstName &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--lastname&lt;/span&gt; YourLastName &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--role&lt;/span&gt; Admin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--email&lt;/span&gt; your@email.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will be prompted to set a password. Type it carefully - input is hidden (no asterisks shown) in the terminal. Confirm it when prompted.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Remember this password - there's no plain-text copy stored anywhere. If you forget it, reset it with &lt;code&gt;airflow users reset-password --username admin&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 10: Start the Airflow API Server and Scheduler
&lt;/h2&gt;

&lt;p&gt;Airflow 3 requires &lt;strong&gt;three processes&lt;/strong&gt; running simultaneously - the &lt;strong&gt;API server&lt;/strong&gt;, the &lt;strong&gt;scheduler&lt;/strong&gt;, and optionally the &lt;strong&gt;dag-processor&lt;/strong&gt; (for larger setups). Open separate WSL terminal tabs or windows for each.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Terminal 1 - Start the API server:&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="nb"&gt;cd&lt;/span&gt; ~/airflow-3.1.3
&lt;span class="nb"&gt;source env&lt;/span&gt;/bin/activate
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AIRFLOW_HOME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;~/airflow-3.1.3/airflow_home
airflow api-server &lt;span class="nt"&gt;--port&lt;/span&gt; 8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Terminal 2 - Start the scheduler:&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="nb"&gt;cd&lt;/span&gt; ~/airflow-3.1.3
&lt;span class="nb"&gt;source env&lt;/span&gt;/bin/activate
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AIRFLOW_HOME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;~/airflow-3.1.3/airflow_home
airflow scheduler
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; In Airflow 3, &lt;code&gt;airflow webserver&lt;/code&gt; has been replaced by &lt;code&gt;airflow api-server&lt;/code&gt;. This is one of the key architectural changes from Airflow 2.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When the API server starts successfully you will see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;INFO:     Uvicorn running on http://0.0.0.0:8080 (Press CTRL+C to quit)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And when the scheduler starts you will see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;INFO:     Starting the scheduler
INFO:     Loaded executor: LocalExecutor
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 11: Access the Airflow UI
&lt;/h2&gt;

&lt;p&gt;Open your browser and navigate 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://localhost:8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will see the new &lt;strong&gt;Airflow 3 React-based login page&lt;/strong&gt;. Enter the username and password you created in Step 9.&lt;/p&gt;

&lt;p&gt;You should now be inside the &lt;strong&gt;Airflow 3 Dashboard&lt;/strong&gt; - ready to create and manage your DAGs!&lt;/p&gt;




&lt;h2&gt;
  
  
  Quick Reference: Starting Airflow After a Reboot
&lt;/h2&gt;

&lt;p&gt;Every time you restart your machine and want to use Airflow, run these commands in separate terminals:&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;# All terminals: navigate, activate venv, and set AIRFLOW_HOME&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/airflow-3.1.3
&lt;span class="nb"&gt;source env&lt;/span&gt;/bin/activate
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AIRFLOW_HOME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;~/airflow-3.1.3/airflow_home

&lt;span class="c"&gt;# Terminal 1&lt;/span&gt;
airflow api-server &lt;span class="nt"&gt;--port&lt;/span&gt; 8080

&lt;span class="c"&gt;# Terminal 2&lt;/span&gt;
airflow scheduler
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;If you added &lt;code&gt;AIRFLOW_HOME&lt;/code&gt; to your &lt;code&gt;~/.bashrc&lt;/code&gt; in Step 3, the export is automatic and you can skip that line.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Airflow 3 vs Airflow 2 - Key Differences
&lt;/h2&gt;

&lt;p&gt;If you're familiar with Airflow 2, here's what changed:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Airflow 2.x&lt;/th&gt;
&lt;th&gt;Airflow 3.x&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;airflow webserver&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;airflow api-server&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;airflow db init&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;airflow db migrate&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;User management built into core&lt;/td&gt;
&lt;td&gt;Requires &lt;code&gt;apache-airflow-providers-fab&lt;/code&gt; + FAB set as auth manager&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SequentialExecutor (default)&lt;/td&gt;
&lt;td&gt;LocalExecutor (default)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;execution_date&lt;/code&gt; in DAG context&lt;/td&gt;
&lt;td&gt;&lt;code&gt;logical_date&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Flask-based UI&lt;/td&gt;
&lt;td&gt;React-based UI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SubDAGs supported&lt;/td&gt;
&lt;td&gt;SubDAGs removed (use TaskGroups)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SLA support&lt;/td&gt;
&lt;td&gt;SLAs removed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




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

&lt;h3&gt;
  
  
  &lt;code&gt;airflow users&lt;/code&gt; command not found / invalid choice: 'users'
&lt;/h3&gt;

&lt;p&gt;This has two possible causes - fix them in order:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cause 1 - FAB provider not installed:&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;pip &lt;span class="nb"&gt;install &lt;/span&gt;apache-airflow-providers-fab &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--constraint&lt;/span&gt; &lt;span class="s2"&gt;"https://raw.githubusercontent.com/apache/airflow/constraints-3.1.3/constraints-3.12.txt"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Cause 2 - Wrong auth manager set (most common):&lt;/strong&gt;&lt;br&gt;
Check your current auth manager:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;airflow config get-value core auth_manager
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If it shows &lt;code&gt;SimpleAuthManager&lt;/code&gt;, switch it to FAB in &lt;code&gt;airflow.cfg&lt;/code&gt; - see Step 7b above. This is the most common cause of the &lt;code&gt;users&lt;/code&gt; command missing even after the FAB provider is installed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deprecation warnings about &lt;code&gt;[webserver]&lt;/code&gt; settings
&lt;/h3&gt;

&lt;p&gt;These occur when Airflow 3 picks up a config file from an old Airflow 2 installation. Make sure &lt;code&gt;AIRFLOW_HOME&lt;/code&gt; points to a fresh directory and not &lt;code&gt;~/airflow&lt;/code&gt; (the Airflow 2 default).&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;No module named 'airflow.providers.fab'&lt;/code&gt; warning
&lt;/h3&gt;

&lt;p&gt;The FAB provider isn't installed. See Step 7 above.&lt;/p&gt;

&lt;h3&gt;
  
  
  Forgot your admin password
&lt;/h3&gt;

&lt;p&gt;Reset it without deleting the user:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;airflow &lt;span class="nb"&gt;users &lt;/span&gt;reset-password &lt;span class="nt"&gt;--username&lt;/span&gt; admin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Port 8080 already in use
&lt;/h3&gt;

&lt;p&gt;Either stop the other process using port 8080, or start the API server on a different port:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;airflow api-server &lt;span class="nt"&gt;--port&lt;/span&gt; 8081
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Summary of All Commands
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Set AIRFLOW_HOME (permanent)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'export AIRFLOW_HOME=~/airflow-3.1.3/airflow_home'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; ~/.bashrc
&lt;span class="nb"&gt;source&lt;/span&gt; ~/.bashrc

&lt;span class="c"&gt;# Create project directory and virtual environment&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; ~/airflow-3.1.3 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; ~/airflow-3.1.3
python3 &lt;span class="nt"&gt;-m&lt;/span&gt; venv &lt;span class="nb"&gt;env
source env&lt;/span&gt;/bin/activate

&lt;span class="c"&gt;# Install Airflow with constraints&lt;/span&gt;
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--upgrade&lt;/span&gt; pip
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="s2"&gt;"apache-airflow==3.1.3"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--constraint&lt;/span&gt; &lt;span class="s2"&gt;"https://raw.githubusercontent.com/apache/airflow/constraints-3.1.3/constraints-3.12.txt"&lt;/span&gt;

&lt;span class="c"&gt;# Install FAB auth manager provider&lt;/span&gt;
pip &lt;span class="nb"&gt;install &lt;/span&gt;apache-airflow-providers-fab &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--constraint&lt;/span&gt; &lt;span class="s2"&gt;"https://raw.githubusercontent.com/apache/airflow/constraints-3.1.3/constraints-3.12.txt"&lt;/span&gt;

&lt;span class="c"&gt;# Verify Airflow version&lt;/span&gt;
airflow version

&lt;span class="c"&gt;# Set FAB as auth manager in airflow.cfg (under [core] section)&lt;/span&gt;
&lt;span class="c"&gt;# auth_manager = airflow.providers.fab.auth_manager.fab_auth_manager.FabAuthManager&lt;/span&gt;

&lt;span class="c"&gt;# Verify auth manager is set correctly&lt;/span&gt;
airflow config get-value core auth_manager

&lt;span class="c"&gt;# Initialize database&lt;/span&gt;
airflow db migrate

&lt;span class="c"&gt;# Create admin user&lt;/span&gt;
airflow &lt;span class="nb"&gt;users &lt;/span&gt;create &lt;span class="nt"&gt;--username&lt;/span&gt; admin &lt;span class="nt"&gt;--firstname&lt;/span&gt; YourFirstName &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--lastname&lt;/span&gt; YourLastName &lt;span class="nt"&gt;--role&lt;/span&gt; Admin &lt;span class="nt"&gt;--email&lt;/span&gt; your@email.com

&lt;span class="c"&gt;# Start services (separate terminals)&lt;/span&gt;
airflow api-server &lt;span class="nt"&gt;--port&lt;/span&gt; 8080    &lt;span class="c"&gt;# Terminal 1&lt;/span&gt;
airflow scheduler                  &lt;span class="c"&gt;# Terminal 2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;Now that Airflow 3.1.3 is running, here are some things to explore next:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Write your first DAG&lt;/strong&gt; - create a Python file in &lt;code&gt;$AIRFLOW_HOME/dags/&lt;/code&gt; to define a workflow&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Explore the new UI&lt;/strong&gt; - Airflow 3 ships with a brand new React-based interface with improved DAG views&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Learn about Assets&lt;/strong&gt; - Airflow 3 introduces Assets (formerly Datasets) for data-aware scheduling&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set up PostgreSQL&lt;/strong&gt; - replace SQLite with PostgreSQL as the Airflow metadata backend for production use&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use the Task SDK&lt;/strong&gt; - Airflow 3 ships with a standalone Task SDK for writing tasks in isolation&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;For the official Airflow 3 Quick Start guide, visit: &lt;a href="https://airflow.apache.org/docs/apache-airflow/stable/start.html" rel="noopener noreferrer"&gt;https://airflow.apache.org/docs/apache-airflow/stable/start.html&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Written based on a real installation walkthrough on WSL2 Ubuntu 24.04 (Noble) with MobiXterm.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>linux</category>
      <category>ai</category>
      <category>beginners</category>
      <category>programming</category>
    </item>
    <item>
      <title>How to Build an AI Agent (with Python Example)</title>
      <dc:creator>Sospeter Mong'are</dc:creator>
      <pubDate>Tue, 30 Jun 2026 08:33:14 +0000</pubDate>
      <link>https://dev.to/msnmongare/how-to-build-an-ai-agent-with-python-example-12jb</link>
      <guid>https://dev.to/msnmongare/how-to-build-an-ai-agent-with-python-example-12jb</guid>
      <description>&lt;p&gt;Artificial Intelligence is changing the way we build software. Instead of creating applications that simply follow predefined instructions, developers can now build AI agents that understand goals, make decisions, use tools, and complete tasks with minimal human intervention.&lt;/p&gt;

&lt;p&gt;An AI agent is much more than a chatbot. It is a software system that can think through a problem, decide what actions to take, interact with external tools, remember previous interactions, and achieve a specific objective.&lt;/p&gt;

&lt;p&gt;This guide walks you through the complete process of building an AI agent using the roadmap outlined below.&lt;/p&gt;




&lt;h1&gt;
  
  
  What is an AI Agent?
&lt;/h1&gt;

&lt;p&gt;An AI agent is a program that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Understands a user's request&lt;/li&gt;
&lt;li&gt;Plans how to solve it&lt;/li&gt;
&lt;li&gt;Uses tools when necessary&lt;/li&gt;
&lt;li&gt;Remembers useful information&lt;/li&gt;
&lt;li&gt;Returns the best possible answer&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Instead of asking,&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What's today's weather?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You could ask,&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Check today's weather, then tell me whether I should carry an umbrella and reschedule my outdoor meeting."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;An AI agent can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Understand the goal&lt;/li&gt;
&lt;li&gt;Call a weather API&lt;/li&gt;
&lt;li&gt;Analyze the forecast&lt;/li&gt;
&lt;li&gt;Make a recommendation&lt;/li&gt;
&lt;li&gt;Respond naturally&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Step 1: Define the Purpose and Scope
&lt;/h1&gt;

&lt;p&gt;Every AI agent starts with one important question:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What problem is this agent solving?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Without a clear purpose, the agent becomes confusing and unreliable.&lt;/p&gt;

&lt;p&gt;Ask yourself:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Who will use it?&lt;/li&gt;
&lt;li&gt;What tasks should it perform?&lt;/li&gt;
&lt;li&gt;What should it never do?&lt;/li&gt;
&lt;li&gt;How will success be measured?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Example
&lt;/h3&gt;

&lt;p&gt;Suppose you are building a customer support agent.&lt;/p&gt;

&lt;p&gt;Purpose:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Help customers answer common questions about orders.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Success criteria:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Answers within 5 seconds&lt;/li&gt;
&lt;li&gt;90% accuracy&lt;/li&gt;
&lt;li&gt;Escalates complex issues to humans&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Constraints:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cannot refund orders&lt;/li&gt;
&lt;li&gt;Cannot change passwords&lt;/li&gt;
&lt;li&gt;Cannot access customer payment information&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Step 2: Design the System Prompt
&lt;/h1&gt;

&lt;p&gt;The system prompt is the agent's personality and instruction manual.&lt;/p&gt;

&lt;p&gt;It tells the AI:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Who it is&lt;/li&gt;
&lt;li&gt;What its job is&lt;/li&gt;
&lt;li&gt;What rules it should follow&lt;/li&gt;
&lt;li&gt;How it should respond&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;You are a customer support assistant.

Your job is to answer customer questions politely.

If you don't know the answer,
say you don't know.

Never invent information.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A good prompt often matters more than choosing a larger model.&lt;/p&gt;




&lt;h1&gt;
  
  
  Step 3: Choose an LLM
&lt;/h1&gt;

&lt;p&gt;The Large Language Model (LLM) is the agent's brain.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;GPT&lt;/li&gt;
&lt;li&gt;Claude&lt;/li&gt;
&lt;li&gt;Gemini&lt;/li&gt;
&lt;li&gt;Llama&lt;/li&gt;
&lt;li&gt;DeepSeek&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Things to consider:&lt;/p&gt;

&lt;h2&gt;
  
  
  Cost
&lt;/h2&gt;

&lt;p&gt;How much does each request cost?&lt;/p&gt;

&lt;h2&gt;
  
  
  Speed
&lt;/h2&gt;

&lt;p&gt;How quickly does it respond?&lt;/p&gt;

&lt;h2&gt;
  
  
  Context Window
&lt;/h2&gt;

&lt;p&gt;How much information can it remember in one conversation?&lt;/p&gt;

&lt;h2&gt;
  
  
  Accuracy
&lt;/h2&gt;

&lt;p&gt;Does it perform well for your task?&lt;/p&gt;

&lt;p&gt;For beginners, cloud-hosted models are the easiest place to start.&lt;/p&gt;




&lt;h1&gt;
  
  
  Step 4: Connect Tools
&lt;/h1&gt;

&lt;p&gt;Without tools, an AI only knows what it has already learned.&lt;/p&gt;

&lt;p&gt;With tools, it can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Search databases&lt;/li&gt;
&lt;li&gt;Call APIs&lt;/li&gt;
&lt;li&gt;Read documents&lt;/li&gt;
&lt;li&gt;Send emails&lt;/li&gt;
&lt;li&gt;Execute code&lt;/li&gt;
&lt;li&gt;Control applications&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Weather Agent&lt;/p&gt;

&lt;p&gt;Tool:&lt;/p&gt;

&lt;p&gt;Weather API&lt;/p&gt;

&lt;p&gt;Finance Agent&lt;/p&gt;

&lt;p&gt;Tool:&lt;/p&gt;

&lt;p&gt;Bank API&lt;/p&gt;

&lt;p&gt;Travel Agent&lt;/p&gt;

&lt;p&gt;Tool:&lt;/p&gt;

&lt;p&gt;Flight API&lt;/p&gt;

&lt;p&gt;Programming Agent&lt;/p&gt;

&lt;p&gt;Tool:&lt;/p&gt;

&lt;p&gt;Python interpreter&lt;/p&gt;

&lt;p&gt;Think of tools as giving your AI "hands."&lt;/p&gt;




&lt;h1&gt;
  
  
  Step 5: Add Memory
&lt;/h1&gt;

&lt;p&gt;Most conversations require remembering previous information.&lt;/p&gt;

&lt;p&gt;There are different types of memory.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conversation Memory
&lt;/h2&gt;

&lt;p&gt;Remember earlier messages.&lt;/p&gt;

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

&lt;p&gt;User:&lt;/p&gt;

&lt;p&gt;"My name is John."&lt;/p&gt;

&lt;p&gt;Later...&lt;/p&gt;

&lt;p&gt;Agent:&lt;/p&gt;

&lt;p&gt;"Hello John."&lt;/p&gt;




&lt;h2&gt;
  
  
  Working Memory
&lt;/h2&gt;

&lt;p&gt;Temporary memory while solving one task.&lt;/p&gt;

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

&lt;p&gt;Reading multiple files before producing a report.&lt;/p&gt;




&lt;h2&gt;
  
  
  Long-Term Memory
&lt;/h2&gt;

&lt;p&gt;Stores information for future conversations.&lt;/p&gt;

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

&lt;p&gt;Favorite programming language.&lt;/p&gt;

&lt;p&gt;Preferred writing style.&lt;/p&gt;

&lt;p&gt;Frequently used APIs.&lt;/p&gt;

&lt;p&gt;Many AI systems store memory using databases or vector databases.&lt;/p&gt;




&lt;h1&gt;
  
  
  Step 6: Orchestrate the Workflow
&lt;/h1&gt;

&lt;p&gt;Real AI agents rarely perform just one action.&lt;/p&gt;

&lt;p&gt;Instead, they execute a workflow.&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;User asks a question

↓

Understand request

↓

Choose tool

↓

Call API

↓

Analyze result

↓

Generate response
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Frameworks like LangGraph and CrewAI help manage these workflows.&lt;/p&gt;




&lt;h1&gt;
  
  
  Step 7: Build a User Interface
&lt;/h1&gt;

&lt;p&gt;Your AI needs a way to interact with users.&lt;/p&gt;

&lt;p&gt;Common interfaces include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Chat applications&lt;/li&gt;
&lt;li&gt;Websites&lt;/li&gt;
&lt;li&gt;Mobile apps&lt;/li&gt;
&lt;li&gt;WhatsApp bots&lt;/li&gt;
&lt;li&gt;Slack bots&lt;/li&gt;
&lt;li&gt;REST APIs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The AI engine stays the same.&lt;/p&gt;

&lt;p&gt;Only the interface changes.&lt;/p&gt;




&lt;h1&gt;
  
  
  Step 8: Test and Improve
&lt;/h1&gt;

&lt;p&gt;Never assume your AI agent is perfect.&lt;/p&gt;

&lt;p&gt;Test:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Wrong inputs&lt;/li&gt;
&lt;li&gt;Missing information&lt;/li&gt;
&lt;li&gt;Slow APIs&lt;/li&gt;
&lt;li&gt;Invalid requests&lt;/li&gt;
&lt;li&gt;Unexpected questions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Measure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Accuracy&lt;/li&gt;
&lt;li&gt;Speed&lt;/li&gt;
&lt;li&gt;User satisfaction&lt;/li&gt;
&lt;li&gt;Cost&lt;/li&gt;
&lt;li&gt;Reliability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then improve continuously.&lt;/p&gt;




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

&lt;p&gt;A complete AI agent usually 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;User
   │
   ▼
User Interface
   │
   ▼
System Prompt
   │
   ▼
Large Language Model
   │
   ▼
Decision Making
   │
   ├──── Weather API
   ├──── Database
   ├──── Email Service
   ├──── Search Engine
   └──── Custom Functions
   │
   ▼
Memory
   │
   ▼
Final Response
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  A Simple AI Agent in Python
&lt;/h1&gt;

&lt;p&gt;Let's build a very simple AI agent using Python and the OpenAI SDK.&lt;/p&gt;

&lt;p&gt;First, install the package:&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;openai
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set your API key:&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;export &lt;/span&gt;&lt;span class="nv"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"your-api-key"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now create the agent.&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;openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OpenAI&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OpenAI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;SYSTEM_PROMPT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
You are a helpful programming assistant.

Explain concepts clearly.

If you don&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;t know something,
say so honestly.
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ask_agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;responses&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-5.5&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&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;role&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;system&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;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SYSTEM_PROMPT&lt;/span&gt;
            &lt;span class="p"&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;role&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;user&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;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;question&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;output_text&lt;/span&gt;

&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

    &lt;span class="n"&gt;question&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;You: &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;question&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;exit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;

    &lt;span class="n"&gt;answer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ask_agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;question&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="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Agent:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You:
Explain Python decorators.

Agent:
Decorators are functions that modify the behavior of another function...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Congratulations!&lt;/p&gt;

&lt;p&gt;You have built your first AI agent.&lt;/p&gt;




&lt;h1&gt;
  
  
  Extending the Agent with a Tool
&lt;/h1&gt;

&lt;p&gt;Suppose we want the agent to answer the current time.&lt;/p&gt;

&lt;p&gt;Instead of asking the AI to guess, we create a Python function.&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;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_current_time&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;%H:%M:%S&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;The agent can call this function whenever the user asks:&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="s2"&gt;"What time is it?"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of making up the answer, it retrieves the real system time.&lt;/p&gt;

&lt;p&gt;This simple idea scales to many real-world tools, such as weather APIs, databases, email services, payment gateways, and search engines.&lt;/p&gt;




&lt;h1&gt;
  
  
  Real-World AI Agent Ideas
&lt;/h1&gt;

&lt;p&gt;Once you understand the building blocks, you can create agents for almost any industry:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Customer support assistant&lt;/li&gt;
&lt;li&gt;WhatsApp chatbot&lt;/li&gt;
&lt;li&gt;Fundraising assistant&lt;/li&gt;
&lt;li&gt;Medical appointment scheduler&lt;/li&gt;
&lt;li&gt;Personal finance advisor&lt;/li&gt;
&lt;li&gt;School management assistant&lt;/li&gt;
&lt;li&gt;HR recruitment assistant&lt;/li&gt;
&lt;li&gt;Sales assistant&lt;/li&gt;
&lt;li&gt;Software debugging assistant&lt;/li&gt;
&lt;li&gt;Content writing assistant&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each follows the same architecture: define a purpose, guide the model with a clear prompt, connect the right tools, maintain useful memory, orchestrate the workflow, provide an interface, and continuously test and improve.&lt;/p&gt;

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

&lt;p&gt;Building an AI agent is not just about connecting an LLM to a chat window. A successful agent is designed around a clear goal, given the right instructions, equipped with tools to interact with the outside world, supported by memory to maintain context, and wrapped in a reliable workflow and user interface.&lt;/p&gt;

&lt;p&gt;The good news is that you do not need to build everything at once. Start with a simple conversational agent, then gradually add tools, memory, and automation as your application grows. By following these eight steps, you will have a solid foundation for creating intelligent AI applications that solve real-world problems.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>beginners</category>
      <category>automation</category>
    </item>
    <item>
      <title>Understanding the Difference between Agents vs Automation</title>
      <dc:creator>Sospeter Mong'are</dc:creator>
      <pubDate>Mon, 29 Jun 2026 06:36:05 +0000</pubDate>
      <link>https://dev.to/msnmongare/understanding-the-difference-between-agents-vs-automation-aoc</link>
      <guid>https://dev.to/msnmongare/understanding-the-difference-between-agents-vs-automation-aoc</guid>
      <description>&lt;p&gt;Artificial Intelligence has brought the term "AI Agent" into almost every technology conversation. As a result, many people now use the words &lt;em&gt;agent&lt;/em&gt; and &lt;em&gt;automation&lt;/em&gt; interchangeably. While both are designed to reduce manual work and improve efficiency, they solve problems in fundamentally different ways.&lt;/p&gt;

&lt;p&gt;Understanding this distinction is essential if you're building software, automating business processes, or deciding where AI fits into your organization.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is Automation?
&lt;/h2&gt;

&lt;p&gt;Automation is designed to execute predefined instructions.&lt;/p&gt;

&lt;p&gt;You tell the system exactly what to do, in what order, and under what conditions. Every time those conditions are met, it performs the same sequence of actions.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;A customer submits a form.&lt;/li&gt;
&lt;li&gt;An email is automatically sent.&lt;/li&gt;
&lt;li&gt;A record is created in the database.&lt;/li&gt;
&lt;li&gt;A notification is sent to the sales team.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every step is predetermined. If the process changes, the workflow must be updated.&lt;/p&gt;

&lt;p&gt;Automation excels at repetitive, predictable tasks where consistency is more important than decision-making.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is an AI Agent?
&lt;/h2&gt;

&lt;p&gt;An AI agent is not focused on following instructions. It is focused on achieving a goal.&lt;/p&gt;

&lt;p&gt;Instead of executing a rigid sequence of steps, an agent observes its environment, evaluates available information, makes decisions, and adjusts its actions as circumstances change.&lt;/p&gt;

&lt;p&gt;If one approach fails, it can try another. If new information becomes available, it can revise its strategy without requiring a developer to define every possible scenario in advance.&lt;/p&gt;

&lt;p&gt;In simple terms:&lt;/p&gt;

&lt;p&gt;Automation asks:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"What steps should I execute?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;An agent asks:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"What is the best way to accomplish this objective?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This ability to reason and adapt is what makes agents fundamentally different from traditional automation.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Simple Example
&lt;/h2&gt;

&lt;p&gt;Imagine you're booking a business trip.&lt;/p&gt;

&lt;p&gt;An automated workflow might:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Book the airline you specified.&lt;/li&gt;
&lt;li&gt;Reserve the hotel you selected.&lt;/li&gt;
&lt;li&gt;Email you the itinerary.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It completes exactly what it was programmed to do.&lt;/p&gt;

&lt;p&gt;An AI agent, however, could:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Compare multiple flights.&lt;/li&gt;
&lt;li&gt;Choose the most cost-effective option.&lt;/li&gt;
&lt;li&gt;Avoid long layovers.&lt;/li&gt;
&lt;li&gt;Ensure arrival before your meeting.&lt;/li&gt;
&lt;li&gt;Rebook automatically if a flight is cancelled.&lt;/li&gt;
&lt;li&gt;Notify you of the changes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The objective isn't to follow a checklist. The objective is to get you to your destination successfully.&lt;/p&gt;

&lt;h2&gt;
  
  
  When Automation Is the Better Choice
&lt;/h2&gt;

&lt;p&gt;Despite the excitement around AI agents, automation remains the right solution for many business processes.&lt;/p&gt;

&lt;p&gt;Choose automation when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The workflow is predictable.&lt;/li&gt;
&lt;li&gt;Business rules rarely change.&lt;/li&gt;
&lt;li&gt;Compliance requires consistent execution.&lt;/li&gt;
&lt;li&gt;Decisions are straightforward.&lt;/li&gt;
&lt;li&gt;Speed and reliability matter more than flexibility.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Payroll processing, invoice generation, scheduled backups, and email notifications are all excellent examples of automation.&lt;/p&gt;

&lt;h2&gt;
  
  
  When an Agent Makes More Sense
&lt;/h2&gt;

&lt;p&gt;An AI agent becomes valuable when the environment is dynamic and decisions cannot be fully predefined.&lt;/p&gt;

&lt;p&gt;Use an agent when your system needs to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reason through complex situations.&lt;/li&gt;
&lt;li&gt;Adapt to changing conditions.&lt;/li&gt;
&lt;li&gt;Work toward goals rather than fixed procedures.&lt;/li&gt;
&lt;li&gt;Handle uncertainty.&lt;/li&gt;
&lt;li&gt;Decide between multiple possible actions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Examples include customer support assistants, intelligent research systems, autonomous coding assistants, network troubleshooting agents, and procurement assistants that negotiate between vendors.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Biggest Mistake
&lt;/h2&gt;

&lt;p&gt;One of the biggest misconceptions in AI today is believing every workflow should become an agent.&lt;/p&gt;

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

&lt;p&gt;Replacing a simple, deterministic process with an AI agent often introduces unnecessary complexity, higher costs, and less predictable behavior.&lt;/p&gt;

&lt;p&gt;Likewise, trying to solve a complex, ever-changing problem with traditional automation can lead to brittle systems that constantly require manual updates.&lt;/p&gt;

&lt;p&gt;The smartest solutions combine both approaches.&lt;/p&gt;

&lt;p&gt;Automation handles the repetitive work, while agents take over where reasoning, judgment, and adaptability are required.&lt;/p&gt;

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

&lt;p&gt;Automation follows instructions.&lt;/p&gt;

&lt;p&gt;Agents pursue outcomes.&lt;/p&gt;

&lt;p&gt;Automation delivers consistency through predefined workflows. Agents deliver adaptability through intelligent decision-making.&lt;/p&gt;

&lt;p&gt;Neither is universally better. They solve different problems.&lt;/p&gt;

&lt;p&gt;The future of software isn't choosing agents over automation. It's understanding when each is the right tool for the job, and combining them to build systems that are both efficient and intelligent.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>automation</category>
      <category>agentskills</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How to Tell If a Task Needs an Agent or an Automation</title>
      <dc:creator>Sospeter Mong'are</dc:creator>
      <pubDate>Sat, 27 Jun 2026 08:18:57 +0000</pubDate>
      <link>https://dev.to/msnmongare/how-to-tell-if-a-task-needs-an-agent-or-an-automation-5eo6</link>
      <guid>https://dev.to/msnmongare/how-to-tell-if-a-task-needs-an-agent-or-an-automation-5eo6</guid>
      <description>&lt;p&gt;One of the biggest mistakes businesses make when adopting AI is trying to use an AI agent for every problem.&lt;/p&gt;

&lt;p&gt;Not every task needs reasoning. Not every workflow needs an LLM. Sometimes a simple automation is faster, cheaper, and more reliable.&lt;/p&gt;

&lt;p&gt;The question isn't &lt;em&gt;"Can AI do this?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The real question is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Does this task require thinking, or just doing?"&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Rule #1: If the steps are always the same, automate it.
&lt;/h2&gt;

&lt;p&gt;Traditional automation excels when the workflow is predictable.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Sending a welcome email after signup&lt;/li&gt;
&lt;li&gt;Copying files between systems&lt;/li&gt;
&lt;li&gt;Moving data from one database to another&lt;/li&gt;
&lt;li&gt;Creating invoices every month&lt;/li&gt;
&lt;li&gt;Backing up databases&lt;/li&gt;
&lt;li&gt;Syncing CRM records&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These tasks follow clear rules.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Trigger → Execute Steps → Finish
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No decisions.&lt;br&gt;
No reasoning.&lt;br&gt;
No uncertainty.&lt;/p&gt;

&lt;p&gt;This is where tools like Power Automate, Azure Logic Apps, n8n, Airflow, Zapier, or simple scripts shine.&lt;/p&gt;


&lt;h2&gt;
  
  
  Rule #2: If the task requires judgment, use an AI agent.
&lt;/h2&gt;

&lt;p&gt;An agent becomes valuable when there isn't a fixed sequence of actions.&lt;/p&gt;

&lt;p&gt;Instead, it has to decide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What information is needed?&lt;/li&gt;
&lt;li&gt;Which tool should I use?&lt;/li&gt;
&lt;li&gt;Is the answer sufficient?&lt;/li&gt;
&lt;li&gt;Should I ask the user another question?&lt;/li&gt;
&lt;li&gt;Do I need to search somewhere else?&lt;/li&gt;
&lt;/ul&gt;

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

&lt;blockquote&gt;
&lt;p&gt;"Analyze this support ticket, search the customer's purchase history, check whether a refund qualifies under company policy, then draft an appropriate response."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Nobody hard-coded every possible path.&lt;/p&gt;

&lt;p&gt;The agent reasons about the problem before acting.&lt;/p&gt;

&lt;p&gt;Its workflow looks more like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Goal
 ↓
Reason
 ↓
Choose Tool
 ↓
Observe Results
 ↓
Think Again
 ↓
Take Next Action
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Rule #3: If the workflow contains uncertainty, you're entering agent territory.
&lt;/h2&gt;

&lt;p&gt;Ask yourself:&lt;/p&gt;

&lt;p&gt;Can I write every step beforehand?&lt;/p&gt;

&lt;p&gt;If yes...&lt;/p&gt;

&lt;p&gt;Automation.&lt;/p&gt;

&lt;p&gt;If no...&lt;/p&gt;

&lt;p&gt;An agent may be the better solution.&lt;/p&gt;

&lt;p&gt;Consider these examples.&lt;/p&gt;

&lt;p&gt;Automation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Every invoice goes to Finance."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Agent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Read the invoice and determine which department should approve it."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Automation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Archive every email older than 90 days."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Agent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Read the email and determine whether it's important enough to retain."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Automation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Generate a report every Monday."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Agent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Review this week's KPIs and explain the biggest business risks."&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Rule #4: If the task only follows instructions, don't use AI.
&lt;/h2&gt;

&lt;p&gt;Many teams build expensive AI systems that simply execute fixed logic.&lt;/p&gt;

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

&lt;p&gt;If you already know the exact sequence of actions, a workflow engine is usually the better choice.&lt;/p&gt;

&lt;p&gt;AI should solve uncertainty, not replace an if-statement.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule #5: Sometimes the best solution is both.
&lt;/h2&gt;

&lt;p&gt;The most powerful enterprise systems combine automation with AI agents.&lt;/p&gt;

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

&lt;ol&gt;
&lt;li&gt;A customer submits a request.&lt;/li&gt;
&lt;li&gt;An automation triggers the workflow.&lt;/li&gt;
&lt;li&gt;An AI agent classifies the request.&lt;/li&gt;
&lt;li&gt;The agent decides which internal systems to query.&lt;/li&gt;
&lt;li&gt;Automation performs the approved actions.&lt;/li&gt;
&lt;li&gt;The workflow updates the CRM and sends notifications.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Notice the difference.&lt;/p&gt;

&lt;p&gt;Automation handles execution.&lt;/p&gt;

&lt;p&gt;The agent handles decision-making.&lt;/p&gt;

&lt;p&gt;Each does what it's best at.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Simple Decision Framework
&lt;/h2&gt;

&lt;p&gt;Before building anything, ask these five questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Are the steps predictable every time?&lt;/li&gt;
&lt;li&gt;Does the system need to make decisions?&lt;/li&gt;
&lt;li&gt;Will the workflow change depending on context?&lt;/li&gt;
&lt;li&gt;Does it need to understand natural language?&lt;/li&gt;
&lt;li&gt;Does it need to choose between multiple tools or actions?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your answers are mostly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Yes&lt;/strong&gt; to predictable steps -&amp;gt; &lt;strong&gt;Automation&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Yes&lt;/strong&gt; to reasoning and decision-making -&amp;gt; &lt;strong&gt;Agent&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Final Thought
&lt;/h2&gt;

&lt;p&gt;Don't start with AI.&lt;/p&gt;

&lt;p&gt;Start with the problem.&lt;/p&gt;

&lt;p&gt;If the work is repetitive, automate it.&lt;/p&gt;

&lt;p&gt;If the work requires understanding, reasoning, planning, and choosing the next action, build an agent.&lt;/p&gt;

&lt;p&gt;The smartest systems aren't the ones with the most AI.&lt;/p&gt;

&lt;p&gt;They're the ones that know when &lt;strong&gt;not&lt;/strong&gt; to use it.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>beginners</category>
      <category>python</category>
    </item>
    <item>
      <title>How to Apply for a Safaricom Paybill or Till Number</title>
      <dc:creator>Sospeter Mong'are</dc:creator>
      <pubDate>Wed, 24 Jun 2026 21:01:58 +0000</pubDate>
      <link>https://dev.to/msnmongare/how-to-apply-for-a-safaricom-paybill-or-till-number-426</link>
      <guid>https://dev.to/msnmongare/how-to-apply-for-a-safaricom-paybill-or-till-number-426</guid>
      <description>&lt;p&gt;If you run a business in Kenya, accepting &lt;a href="https://www.safaricom.co.ke/main-mpesa/m-pesa-services" rel="noopener noreferrer"&gt;M-PESA&lt;/a&gt; payments is no longer optional - it's a necessity. Whether you're a retailer, school, SACCO, freelancer, or landlord, having your own &lt;strong&gt;&lt;a href="https://www.safaricom.co.ke/main-mpesa/for-your-business/business-paybill" rel="noopener noreferrer"&gt;Paybill&lt;/a&gt;&lt;/strong&gt; or &lt;strong&gt;&lt;a href="https://www.safaricom.co.ke/main-mpesa/for-your-business/business-till" rel="noopener noreferrer"&gt;Till number&lt;/a&gt;&lt;/strong&gt; gives you a professional, trackable channel for receiving funds from customers.&lt;/p&gt;

&lt;p&gt;This guide walks you through everything: what each product is, what documents you need, and the exact steps to apply - online or in person.&lt;/p&gt;




&lt;h2&gt;
  
  
  Paybill vs. Till Number - What's the Difference?
&lt;/h2&gt;

&lt;p&gt;Before applying, you need to know which product fits your business.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.safaricom.co.ke/main-mpesa/for-your-business/business-paybill" rel="noopener noreferrer"&gt;Paybill Number&lt;/a&gt;&lt;/strong&gt; is a business number that lets organizations collect payments from customers on a regular basis via M-PESA. When a customer pays, they enter both your Paybill number and an &lt;strong&gt;account reference&lt;/strong&gt; (such as a meter number, invoice ID, student name, or membership number). This makes Paybill ideal for businesses that need to identify individual payers - schools, utilities, insurance firms, SACCOs, hospitals, and government agencies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.safaricom.co.ke/main-mpesa/for-your-business/business-till" rel="noopener noreferrer"&gt;Till Number (Buy Goods)&lt;/a&gt;&lt;/strong&gt; is a unique code that lets customers pay for goods and services directly into your M-PESA account. The customer only enters your Till number - no account reference needed. It's faster and simpler, and is the go-to choice for retail shops, restaurants, supermarkets, boutiques, fuel stations, and online sellers.&lt;/p&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;Paybill&lt;/th&gt;
&lt;th&gt;Till Number&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Account reference required&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;Ideal for&lt;/td&gt;
&lt;td&gt;Regular collections, utilities, schools&lt;/td&gt;
&lt;td&gt;Point-of-sale, retail, services&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Settlement&lt;/td&gt;
&lt;td&gt;Bank account&lt;/td&gt;
&lt;td&gt;Owner's M-PESA or bank&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Transaction fee (standard)&lt;/td&gt;
&lt;td&gt;Varies by tariff&lt;/td&gt;
&lt;td&gt;0.5%, capped at KSh 200&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Paybill Products Available
&lt;/h2&gt;

&lt;p&gt;When applying for a Paybill, Safaricom offers two variants:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Long-Term Paybill&lt;/strong&gt; - The standard product for established businesses, institutions, and government entities. Requires valid business/company registration documents and is intended for ongoing, indefinite use.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://m-pesaforbusiness.co.ke/apply" rel="noopener noreferrer"&gt;Short-Term Paybill&lt;/a&gt;&lt;/strong&gt; - Designed for a specific project or purpose and active for a maximum of six months, after which it is closed. Suitable for contractors, event organizers, or project-based work. A short-term paybill is a product that allows for fundraising of funds for different needs, e.g. medical bills, funerals, weddings, education purposes, and for charity/catastrophes e.g. Fund, Funeral Fund, Education Fund.&lt;/p&gt;




&lt;h2&gt;
  
  
  What You Need Before Applying
&lt;/h2&gt;

&lt;p&gt;Regardless of which product you choose, there are common prerequisites:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;registered Safaricom line&lt;/strong&gt; in your name (this becomes your nominated number for receiving SMS notifications and withdrawing funds)&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;business bank account&lt;/strong&gt; with a cancelled cheque or bank letter confirming your account details - this is where your funds will settle&lt;/li&gt;
&lt;li&gt;Valid &lt;strong&gt;identification documents&lt;/strong&gt; (National ID, Passport, Alien ID, or Military ID)&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;&lt;a href="https://drive.google.com/file/d/12H4fxUFoyhuJzcAEEoJ5kSVpGK-wlZcf/view?usp=sharing" rel="noopener noreferrer"&gt;duly filled and signed Safaricom application form&lt;/a&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Beyond these basics, the documents required differ by business structure.&lt;/p&gt;




&lt;h2&gt;
  
  
  Document Requirements by Business Type
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Individual / Sole Proprietor
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;National ID or Passport (front and back)&lt;/li&gt;
&lt;li&gt;Business permit or certificate of registration&lt;/li&gt;
&lt;li&gt;Cancelled cheque or bank letter with account details&lt;/li&gt;
&lt;li&gt;KRA PIN certificate&lt;/li&gt;
&lt;li&gt;&lt;a href="https://drive.google.com/file/d/12H4fxUFoyhuJzcAEEoJ5kSVpGK-wlZcf/view?usp=sharing" rel="noopener noreferrer"&gt;Signed application form and M-PESA terms and conditions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If your business permit does not match your identification documents, you may also need to provide a Certificate of Business Registration to prove the relationship between you and the business.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Limited Liability Company (LLC)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Certificate of Incorporation&lt;/li&gt;
&lt;li&gt;CR12 (must be less than 3 months old for new applications; less than 2 years old for additional tills under an existing account)&lt;/li&gt;
&lt;li&gt;KRA PIN certificate for the company&lt;/li&gt;
&lt;li&gt;Board resolution authorizing the application and the nominated signatories&lt;/li&gt;
&lt;li&gt;Beneficial Ownership Form (BOF1) - required for BRS-registered companies; non-BRS companies complete a Safaricom-issued self-declaration template&lt;/li&gt;
&lt;li&gt;National IDs for all directors listed on CR12&lt;/li&gt;
&lt;li&gt;Cancelled cheque or bank letter&lt;/li&gt;
&lt;li&gt;&lt;a href="https://drive.google.com/file/d/12H4fxUFoyhuJzcAEEoJ5kSVpGK-wlZcf/view?usp=sharing" rel="noopener noreferrer"&gt;Signed application form&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Partnership
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Business registration certificate or business permit&lt;/li&gt;
&lt;li&gt;Partnership deed or affidavit&lt;/li&gt;
&lt;li&gt;KRA PIN certificates for the partnership and partners&lt;/li&gt;
&lt;li&gt;National IDs of all partners&lt;/li&gt;
&lt;li&gt;M-PESA Authorization form signed by all partners (required where more than two persons are named as business owners)&lt;/li&gt;
&lt;li&gt;Cancelled cheque or bank letter&lt;/li&gt;
&lt;li&gt;&lt;a href="https://drive.google.com/file/d/12H4fxUFoyhuJzcAEEoJ5kSVpGK-wlZcf/view?usp=sharing" rel="noopener noreferrer"&gt;Signed application form&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  NGO / Church / Religious Organization
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Certificate of Registration from the NGO Coordination Board (for NGOs) or relevant authority (for churches)&lt;/li&gt;
&lt;li&gt;Constitution or trust deed&lt;/li&gt;
&lt;li&gt;Board resolution authorizing the account&lt;/li&gt;
&lt;li&gt;KRA PIN certificate&lt;/li&gt;
&lt;li&gt;National IDs of authorized signatories&lt;/li&gt;
&lt;li&gt;Cancelled cheque or bank letter&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  SACCO / Cooperative Society
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Certificate of Registration from the Commissioner for Co-operatives&lt;/li&gt;
&lt;li&gt;SACCO bylaws&lt;/li&gt;
&lt;li&gt;Board resolution&lt;/li&gt;
&lt;li&gt;KRA PIN certificate&lt;/li&gt;
&lt;li&gt;National IDs of authorized signatories&lt;/li&gt;
&lt;li&gt;Cancelled cheque or bank letter&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Pro tip:&lt;/strong&gt; All documents should be in PDF format and hand-filled(not computer-filled). Safaricom accepts up to 10 MB per document. ZIP files are not accepted.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  How to Apply Online (Self-Onboarding Portal)- for M-PESA Business Till (Buy Goods Till) or Short-term Paybill
&lt;/h2&gt;

&lt;p&gt;This is the fastest route for most applicants.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Visit the Portal&lt;/strong&gt;&lt;br&gt;
Go to &lt;a href="https://m-pesaforbusiness.co.ke" rel="noopener noreferrer"&gt;m-pesaforbusiness.co.ke&lt;/a&gt; and click &lt;strong&gt;Apply Now&lt;/strong&gt;.&lt;/p&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqzzdhkzfr2j3hwfne5vk.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqzzdhkzfr2j3hwfne5vk.png" alt="Lipa na Mpesa Application Portal" width="799" height="528"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Select Your Product&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For a &lt;strong&gt;Till Number&lt;/strong&gt;: click &lt;em&gt;M-PESA Business Till (Buy Goods Till)&lt;/em&gt;, then &lt;em&gt;Apply Now&lt;/em&gt;. Choose between &lt;em&gt;Individual Till&lt;/em&gt; (funds settle to the owner's M-PESA account) or &lt;em&gt;Business Till&lt;/em&gt; (funds settle to a bank account). Select your preferred option.&lt;/li&gt;
&lt;/ul&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwkx4hafxdkycxidi3k3l.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwkx4hafxdkycxidi3k3l.png" alt="Business Till" width="800" height="461"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For a &lt;strong&gt;Paybill&lt;/strong&gt;: Click &lt;em&gt;Short-Term Paybill&lt;/em&gt; in the product list. Choose any of the items presented i.e. Medical Fund, Education Fund, Funeral Fund, Wedding Fund, Charity / Catastrophe then &lt;em&gt;Apply Now&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdkk7kizdyd0ju63fxlb0.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdkk7kizdyd0ju63fxlb0.png" alt="Short-term Paybill" width="800" height="478"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the Application Form attached here: &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Fill in the Application Form&lt;/strong&gt;&lt;br&gt;
Complete all required fields - business name, physical address, nominated mobile number, administrator details, bank account details, and your preferred tariff (for Paybill) and other details required.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4: Choose Your Tariff (Paybill Only)&lt;/strong&gt;&lt;br&gt;
Safaricom offers three tariff options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Mgao&lt;/strong&gt; - fees shared between customer and business; suitable for small startups&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Business Bouquet&lt;/strong&gt; - business absorbs all transaction fees; ideal for e-commerce and service providers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Customer Bouquet&lt;/strong&gt; - customer pays all fees; typically used by utilities, schools, and SACCOs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 5: Upload Documents (Both for Online portal and email)&lt;/strong&gt;&lt;br&gt;
Attach your required documents based on your business type (see above). All files must be in PDF format, each under 10 MB.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 6: Submit&lt;/strong&gt;&lt;br&gt;
Once submitted, you'll receive a confirmation message acknowledging your application. Processing typically takes &lt;strong&gt;24-72 hours&lt;/strong&gt; after all documents are successfully submitted and verified.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 7: Track Your Application&lt;/strong&gt;&lt;br&gt;
Log in to the portal at &lt;a href="https://m-pesaforbusiness.co.ke" rel="noopener noreferrer"&gt;m-pesaforbusiness.co.ke&lt;/a&gt;, click on &lt;strong&gt;My Applications&lt;/strong&gt;, and enter the mobile number used during application. Statuses to look out for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Under Review&lt;/em&gt; - application received and being assessed&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Pending Documents&lt;/em&gt; - more information required from you&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Approved&lt;/em&gt; - documents accepted, number being processed&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Complete&lt;/em&gt; - your number is active and ready to receive payments&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  How to Apply In Person
&lt;/h2&gt;

&lt;p&gt;If you prefer a physical submission or don't have all documents in digital form:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Download and fill the &lt;a href="https://drive.google.com/file/d/12H4fxUFoyhuJzcAEEoJ5kSVpGK-wlZcf/view?usp=sharing" rel="noopener noreferrer"&gt;application form &lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Sign the M-PESA terms and conditions&lt;/li&gt;
&lt;li&gt;Attach copies of all required documents&lt;/li&gt;
&lt;li&gt;Visit any &lt;strong&gt;Safaricom Shop&lt;/strong&gt; or &lt;strong&gt;Care Centre&lt;/strong&gt; countrywide and submit the package&lt;/li&gt;
&lt;li&gt;Alternatively, email your completed application to &lt;strong&gt;&lt;a href="mailto:M-PESABusiness@safaricom.co.ke"&gt;M-PESABusiness@safaricom.co.ke&lt;/a&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Application For Paybill Accepted
&lt;/h2&gt;

&lt;p&gt;Once the application has been accepted you will receive an email confirming that your account has been created&lt;/p&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftz3r2r9jj1o9qv7k5hor.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftz3r2r9jj1o9qv7k5hor.png" alt="Create Organization Operator" width="800" height="297"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  How to Activate Your Till / Paybill After Approval
&lt;/h2&gt;

&lt;p&gt;Once approved, you'll receive your Till/Store number and Operator ID via SMS on your nominated number.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For a Till Number:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Dial &lt;code&gt;*234#&lt;/code&gt; from your nominated Safaricom number&lt;/li&gt;
&lt;li&gt;Select &lt;em&gt;M-PESA Business Till&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Enter your Store number and accept the terms and conditions&lt;/li&gt;
&lt;li&gt;You'll receive an SMS with your Operator ID and start key&lt;/li&gt;
&lt;li&gt;Dial &lt;code&gt;*234*2#&lt;/code&gt; again → go to &lt;em&gt;Account Services&lt;/em&gt; → &lt;em&gt;Change PIN&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Log into the M-PESA Business App or portal and set a new PIN&lt;/li&gt;
&lt;li&gt;You're now live and can receive payments&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;For a Paybill:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Dial &lt;code&gt;*234*4#&lt;/code&gt; from your nominated number&lt;/li&gt;
&lt;li&gt;Follow the on-screen prompts to activate and set your PIN&lt;/li&gt;
&lt;li&gt;You can also manage your Paybill via the M-PESA portal at &lt;a href="https://org.ke.m-pesa.com" rel="noopener noreferrer"&gt;org.ke.m-pesa.com&lt;/a&gt; - credentials are sent to your administrator email upon account creation&lt;/li&gt;
&lt;/ol&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqk81mtmo6pl9fxg2vix1.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqk81mtmo6pl9fxg2vix1.png" alt="Paybill Account Created" width="800" height="297"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Withdrawing Funds
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;From a Till Number:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Dial &lt;code&gt;*234#&lt;/code&gt; → &lt;em&gt;M-PESA Business Till&lt;/em&gt; → &lt;em&gt;Withdraw Funds&lt;/em&gt; → choose &lt;em&gt;To the Bank&lt;/em&gt; → enter your Till/Store number. Note that you can only withdraw to the bank account provided during registration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;From a Paybill:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Dial &lt;code&gt;*234#&lt;/code&gt; → &lt;em&gt;Withdraw Cash&lt;/em&gt; → &lt;em&gt;Bank&lt;/em&gt; → enter your Paybill number. Again, settlement is only to the bank account registered at onboarding.&lt;/p&gt;




&lt;h2&gt;
  
  
  Accessing the M-PESA Business Portal (G2 Portal)
&lt;/h2&gt;

&lt;p&gt;After your number is issued, you'll want access to the M-PESA Business Portal for balance checks, transaction statements, and user management. If Safaricom did not create portal access automatically:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Prepare a letter on your company letterhead addressed to Safaricom&lt;/li&gt;
&lt;li&gt;Include: organization name, Paybill/Till number, administrator's full name, preferred username, and administrator email&lt;/li&gt;
&lt;li&gt;Attach: duly filled and signed Business Administrator Form, company registration certificate(s), and copies of IDs&lt;/li&gt;
&lt;li&gt;Email to &lt;strong&gt;&lt;a href="mailto:lipanampesa@safaricom.co.ke"&gt;lipanampesa@safaricom.co.ke&lt;/a&gt;&lt;/strong&gt; with subject: &lt;em&gt;"Request for M-PESA Portal Login"&lt;/em&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You should receive your credentials within 24–48 hours.&lt;/p&gt;




&lt;h2&gt;
  
  
  For Developers: Taking Your Paybill Live on Daraja
&lt;/h2&gt;

&lt;p&gt;If you're integrating M-PESA payments into an application or website, a Paybill number also grants you access to the &lt;strong&gt;Safaricom Daraja API&lt;/strong&gt;. After your Paybill is approved:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create an account at &lt;a href="https://developer.safaricom.co.ke" rel="noopener noreferrer"&gt;developer.safaricom.co.ke&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Create an app and link your Paybill shortcode&lt;/li&gt;
&lt;li&gt;Follow the Go-Live process at &lt;a href="https://developer.safaricom.co.ke/dashboard/golive" rel="noopener noreferrer"&gt;https://developer.safaricom.co.ke/dashboard/golive&lt;/a&gt; - this includes submitting production credentials and completing a sandbox test run&lt;/li&gt;
&lt;/ol&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjij8zmztjdrrzdvyyaq8.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjij8zmztjdrrzdvyyaq8.png" alt="Go Live" width="800" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Once approved, You will receive passkey in the email. You can use the &lt;a href="https://developer.safaricom.co.ke/dashboard/apis" rel="noopener noreferrer"&gt;Daraja APIs: STK Push (C2B), B2C, Account Balance, Transaction Status, and more&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To learn more on How to Go Live, &lt;a href="https://dev.to/msnmongare/how-to-go-live-on-the-m-pesa-daraja-api-1iip"&gt;Click Here&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Useful Contacts &amp;amp; Links
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Resource&lt;/th&gt;
&lt;th&gt;Details&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Self-Onboarding Portal&lt;/td&gt;
&lt;td&gt;&lt;a href="https://m-pesaforbusiness.co.ke" rel="noopener noreferrer"&gt;m-pesaforbusiness.co.ke&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;M-PESA Business Portal&lt;/td&gt;
&lt;td&gt;&lt;a href="https://org.ke.m-pesa.com" rel="noopener noreferrer"&gt;org.ke.m-pesa.com&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Daraja Developer Portal&lt;/td&gt;
&lt;td&gt;&lt;a href="https://developer.safaricom.co.ke" rel="noopener noreferrer"&gt;developer.safaricom.co.ke&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Email Support&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:M-PESABusiness@safaricom.co.ke"&gt;M-PESABusiness@safaricom.co.ke&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Portal Access Requests&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:lipanampesa@safaricom.co.ke"&gt;lipanampesa@safaricom.co.ke&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Till/Paybill USSD&lt;/td&gt;
&lt;td&gt;&lt;code&gt;*234#&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Customer Care&lt;/td&gt;
&lt;td&gt;100 (Safaricom line)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Final Checklist Before You Apply
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Business registered and permit/certificate available&lt;/li&gt;
&lt;li&gt;[ ] Valid ID for all directors/owners&lt;/li&gt;
&lt;li&gt;[ ] KRA PIN certificate ready (business and individual)&lt;/li&gt;
&lt;li&gt;[ ] Bank letter or cancelled cheque prepared&lt;/li&gt;
&lt;li&gt;[ ] Registered Safaricom SIM in your name&lt;/li&gt;
&lt;li&gt;[ ] Application form downloaded and fully filled&lt;/li&gt;
&lt;li&gt;[ ] All documents in PDF format, under 10 MB each&lt;/li&gt;
&lt;li&gt;[ ] Tariff selected (Paybill applicants)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With the right documents in hand, the online process takes less than 30 minutes and approval comes through within 24 hours. Take your time on the paperwork - missing a single document is the most common reason for delays.&lt;/p&gt;




&lt;h2&gt;
  
  
  Need M-PESA Integration for Your App or System?
&lt;/h2&gt;

&lt;p&gt;Got your Paybill or Till number and now need to integrate it into your website, app, or backend system? I can help.&lt;/p&gt;

&lt;p&gt;I offer M-PESA integration services covering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;STK Push (Mpesa Express)&lt;/strong&gt; -let customers pay directly from your app or website&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;B2C Payments&lt;/strong&gt; - send money to customers, suppliers, or staff programmatically&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Customer to Business (C2B)&lt;/strong&gt; - reliably process and confirm M-PESA transactions in real time&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Daraja API setup &amp;amp; go-live&lt;/strong&gt; - from sandbox to production&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;KCB Buni, PesaLink &amp;amp; other Kenyan and international payment APIs&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;feel free to reach out:&lt;/p&gt;

&lt;p&gt;📧 &lt;strong&gt;Email:&lt;/strong&gt; &lt;a href="mailto:sosmongare@gmail.com"&gt;sosmongare@gmail.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;💬 &lt;strong&gt;WhatsApp:&lt;/strong&gt; &lt;a href="https://wa.me/254708920430?text=Hi%20Sospeter%2C%20I%20read%20your%20article%20on%20Safaricom%20Paybill%2FTill%20numbers%20and%20I%27d%20like%20help%20with%20M-PESA%20integration." rel="noopener noreferrer"&gt;Chat on WhatsApp&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's build something that works.&lt;/p&gt;

</description>
      <category>safaricom</category>
      <category>mpesa</category>
      <category>api</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How to Go Live on the M-Pesa Daraja API</title>
      <dc:creator>Sospeter Mong'are</dc:creator>
      <pubDate>Mon, 22 Jun 2026 11:55:42 +0000</pubDate>
      <link>https://dev.to/msnmongare/how-to-go-live-on-the-m-pesa-daraja-api-1iip</link>
      <guid>https://dev.to/msnmongare/how-to-go-live-on-the-m-pesa-daraja-api-1iip</guid>
      <description>&lt;p&gt;&lt;em&gt;Sandbox testing done? Here's the exact, step-by-step process to get real production credentials from the Daraja portal - faster than you think.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you've built an STK Push integration that works flawlessly in sandbox and you're wondering "what now?" - this guide is for you. The good news: the new Daraja portal has made going live surprisingly self-service. No lengthy email chains, no weeks of waiting. Just a form, an OTP, a product selection, and your production Consumer Key lands in your apps dashboard. Your Passkey lands straight in your inbox.&lt;/p&gt;

&lt;p&gt;Let's walk through the whole thing from start to finish.&lt;/p&gt;




&lt;h2&gt;
  
  
  What "Going Live" on Daraja Actually Means
&lt;/h2&gt;

&lt;p&gt;&lt;a href="//developer.safaricom.co.ke"&gt;Safaricom's Daraja portal &lt;/a&gt; gives every developer two environments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sandbox&lt;/strong&gt; - fake shortcodes, simulated money, instant access. Perfect for building and breaking things safely.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Production&lt;/strong&gt; - your real Paybill or Till number, real customer money, real consequences if something breaks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;"Going live" is the process of provisioning a &lt;strong&gt;production app&lt;/strong&gt; against your actual business shortcode. Once done, Safaricom creates a new production app under &lt;strong&gt;My Apps&lt;/strong&gt;, gives you a live Consumer Key and Consumer Secret, and emails you your Passkey - the credentials you'll use to call real M-Pesa endpoints.&lt;/p&gt;




&lt;h2&gt;
  
  
  Before You Start: Prerequisites You Actually Need
&lt;/h2&gt;

&lt;p&gt;Don't touch the Go Live button until these are in place. Missing any one will stop you cold mid-process.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. A Daraja Developer Account
&lt;/h3&gt;

&lt;p&gt;Register at &lt;a href="https://developer.safaricom.co.ke" rel="noopener noreferrer"&gt;developer.safaricom.co.ke&lt;/a&gt; as an individual or company if you haven't already. You need to be logged in to access the Go Live menu.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. A Working Sandbox Integration
&lt;/h3&gt;

&lt;p&gt;The Go Live form doesn't ask you to prove it, but it's implicit - you should have already tested your STK Push, C2B, or B2C flow end-to-end in sandbox, including callback handling. Going live against an untested integration is how you end up with real customer money in a broken system.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. A Live Safaricom Business Shortcode
&lt;/h3&gt;

&lt;p&gt;This is the most common blocker. Your Paybill or Till number must already be registered with &lt;a href="//M-PESABusiness@Safaricom.co.ke"&gt;Safaricom's business team&lt;/a&gt; - a completely separate process from the developer portal. Apply for this early; it often takes longer than the technical work. The Go Live form will ask for your shortcode, so have it ready.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Your M-PESA Portal Username
&lt;/h3&gt;

&lt;p&gt;The form requires your &lt;strong&gt;Business Admin or Business Manager username&lt;/strong&gt; from the M-PESA Business Portal (&lt;a href="https://org.ke.m-pesa.com/" rel="noopener noreferrer"&gt;https://org.ke.m-pesa.com/&lt;/a&gt;) - not your Daraja login credentials. This is how Safaricom verifies you're authorized to link that shortcode to a production developer app.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Access to Your Registered Phone Number / Email for OTP
&lt;/h3&gt;

&lt;p&gt;Step 2 of the Go Live flow sends an OTP to the contact details registered on your account. Make sure you have access to it.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. A Live HTTPS Callback URL
&lt;/h3&gt;

&lt;p&gt;For APIs like STK Push (Lipa Na M-Pesa), C2B, and B2C, your ResultURL, ConfirmationURL, and ValidationURL must be publicly accessible over HTTPS with a valid SSL certificate. Localhost and Ngrok tunnels don't belong in production.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step-by-Step: How to Go Live on the Daraja Portal
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Click "Go Live" in the Sidebar
&lt;/h3&gt;

&lt;p&gt;Log in to your Daraja account. On the left sidebar, you'll see the &lt;strong&gt;Go Live&lt;/strong&gt; menu item - it sits between &lt;strong&gt;Test Credentials&lt;/strong&gt; and &lt;strong&gt;APIs&lt;/strong&gt;. Click it.&lt;/p&gt;

&lt;p&gt;The page opens with a clean two-step form: &lt;strong&gt;Organization Information → Enter OTP.&lt;/strong&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 2: Fill in Your Organization Information
&lt;/h3&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhxk4k7y7z8rew5vwq9ra.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhxk4k7y7z8rew5vwq9ra.png" alt="Going Live - page 1" width="800" height="395"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The first screen asks for four fields:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;What to Enter&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Verification Type&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Select &lt;code&gt;Short Code&lt;/code&gt; from the dropdown&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Organization ShortCode&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Your live Paybill, Till Number, Head Office, B2C, or Store Number&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Organization Name&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Your company or business name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;M-PESA Username&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Your Business Admin/Manager username from the M-PESA Business Portal&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Accept Safaricom's Terms and Conditions by ticking the checkbox, then click &lt;strong&gt;Next&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Pro tip:&lt;/strong&gt; Your Organization ShortCode and Organization Name must match exactly what's registered on the M-PESA Business Portal. A mismatch is the most common reason this step fails.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  Step 3: Enter the OTP
&lt;/h3&gt;

&lt;p&gt;Safaricom sends a One-Time Password to your registered contact (phone or email). Enter it on the second screen to verify your identity and authorize the go-live request.&lt;/p&gt;

&lt;p&gt;Didn't receive it? Hit &lt;strong&gt;Resend Code&lt;/strong&gt; - it usually arrives within a minute.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 4: Select Your Production APIs
&lt;/h3&gt;

&lt;p&gt;After OTP verification, you'll land on the &lt;strong&gt;product selection screen&lt;/strong&gt; - this is where you tell Safaricom exactly which M-Pesa APIs your app needs in production. The available products include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Lipa Na M-Pesa Production&lt;/strong&gt; - STK Push and Query (what most apps need for collecting payments)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;C2B v2&lt;/strong&gt; - Customer To Business payments with minimized data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;B2C&lt;/strong&gt; - Business To Customer disbursements (e.g. paying out winnings, salaries, refunds)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;B2B&lt;/strong&gt; - Business Buy Goods, Business Pay Bill, and B2C Account Top Up&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transaction Status&lt;/strong&gt; - Check the status of any M-Pesa transaction&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Account Balance&lt;/strong&gt; - Query your business account balance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reversal&lt;/strong&gt; - Reverse a completed transaction&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic QR&lt;/strong&gt; - Generate QR codes for payments&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;M-Pesa Ratiba&lt;/strong&gt; - Recurring/scheduled payment activation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pull Transactions Prod&lt;/strong&gt; - Pull transaction records via API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mobile Number Validation&lt;/strong&gt; - Validate mobile numbers against M-Pesa&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Tick only what you actually need.&lt;/strong&gt; Don't check everything by default - each API maps specific permissions to your shortcode, and requesting ones you don't need can cause issues, particularly the B2C/C2B split (a C2B shortcode and a B2C shortcode cannot share the same production app).&lt;/p&gt;

&lt;p&gt;Once you've made your selections, click &lt;strong&gt;Submit&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 5: See the Success Screen and Check Your Apps
&lt;/h3&gt;

&lt;p&gt;Daraja immediately shows a &lt;strong&gt;"You have successfully applied for production developer app"&lt;/strong&gt; confirmation. It also tells you:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Please check your email for production API URLs against your selected products. Next, we will direct you to view your apps."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcq0krr3npifkdddyrha7.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcq0krr3npifkdddyrha7.png" alt="Go Live - Success" width="799" height="534"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;View Your Apps&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Under &lt;strong&gt;My Apps&lt;/strong&gt;, you'll now see your new &lt;strong&gt;production app&lt;/strong&gt; separate from your sandbox apps. Open it and you'll find your:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Consumer Key&lt;/strong&gt; (production)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consumer Secret&lt;/strong&gt; (production)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your &lt;strong&gt;Passkey&lt;/strong&gt; for Lipa Na M-Pesa (STK Push) is sent directly to your registered email - check your inbox.&lt;/p&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdw0xk9t98z89rusakcyk.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdw0xk9t98z89rusakcyk.png" alt="STK Push Credentials" width="799" height="403"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Now Update Your Code: The Production Swap Checklist
&lt;/h2&gt;

&lt;p&gt;Getting credentials is one thing. Wiring them up correctly is another. Run through this checklist before you process a single live transaction:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Replace&lt;/strong&gt; your sandbox Consumer Key and Consumer Secret with the production ones from your new app.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Switch your base URL&lt;/strong&gt; from &lt;code&gt;https://sandbox.safaricom.co.ke&lt;/code&gt; to &lt;code&gt;https://api.safaricom.co.ke&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Update your Shortcode&lt;/strong&gt; from the sandbox test shortcode (&lt;code&gt;174379&lt;/code&gt; for STK Push) to your real Paybill/Till number.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Replace your Passkey&lt;/strong&gt; with the one received in your email - it's different from the sandbox passkey.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Regenerate your SecurityCredential&lt;/strong&gt; using Safaricom's &lt;strong&gt;production&lt;/strong&gt; public certificate (not the sandbox one). Using the wrong certificate is the #1 cause of &lt;code&gt;ResultCode 8006&lt;/code&gt; (Initiator locked) errors for B2C and Account Balance APIs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verify your Initiator Name and password&lt;/strong&gt; in the M-PESA Business Portal under the API User section - they must match exactly what you send in B2C/Account Balance requests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Confirm your callback URLs&lt;/strong&gt; are live, HTTPS, publicly accessible, and returning &lt;code&gt;200 OK&lt;/code&gt; with a valid JSON body.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Your First 24 Hours After Go-Live
&lt;/h2&gt;

&lt;p&gt;Production credentials aren't a finish line - they're a starting gun. Do these before you open up to real users:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Run one small real transaction end-to-end.&lt;/strong&gt; Trigger an STK Push to yourself for KES 1 and confirm the full loop: request → customer prompt → payment → callback → your database updates correctly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Log everything.&lt;/strong&gt; Every API request, every response, every callback payload. You'll need this for reconciliation and for any Safaricom support tickets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implement retry logic for callbacks.&lt;/strong&gt; Safaricom's callback delivery isn't guaranteed on the first attempt - networks fail. Build a retry mechanism or use a queue.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Watch your access token expiry.&lt;/strong&gt; Production tokens expire every &lt;strong&gt;3600 seconds&lt;/strong&gt; (1 hour). Never hardcode one - always regenerate before it expires.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set up transaction monitoring.&lt;/strong&gt; Unusual error rates, repeated ResultCode failures, or missing callbacks should alert you before your customers notice.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Common Issues After Going Live
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;ResultCode 8006 - Initiator locked&lt;/strong&gt;&lt;br&gt;
Your SecurityCredential was generated using the sandbox certificate. Regenerate it using the production certificate from the Daraja portal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;STK Push request goes through but no callback arrives&lt;/strong&gt;&lt;br&gt;
Your callback URL isn't reachable from Safaricom's servers. Check that it's HTTPS, publicly accessible, and not blocked by a firewall or Cloudflare rule.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Invalid Access Token" errors&lt;/strong&gt;&lt;br&gt;
Your token expired. Implement automatic token refresh - generate a new one before every request or cache it with an expiry buffer of ~50 minutes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sandbox credentials still working, production returning errors&lt;/strong&gt;&lt;br&gt;
You missed one of the URL swaps. Double-check every URL, not just the base URL - some SDKs construct endpoint paths in ways that can still hit the sandbox host.&lt;/p&gt;




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

&lt;p&gt;The Daraja go-live process on the new portal is genuinely self-service and fast - if your prerequisites are in order, you can go from clicking "Go Live" to having a production Consumer Key in your dashboard in under 10 minutes. The bottleneck is almost never the portal itself; it's having your shortcode registered, your M-PESA Portal username ready, and your callbacks deployed to a live server before you sit down to do this.&lt;/p&gt;

&lt;p&gt;Get those in place, and going live is just a form, an OTP, and a checkbox away.&lt;/p&gt;




&lt;p&gt;If you're looking to integrate M-PESA Daraja into your website, mobile app, ERP, CRM, SACCO system, school system, or any custom platform, feel free to reach out.&lt;/p&gt;

&lt;p&gt;Drop your question in the comments or WhatsApp me directly at &lt;a href="https://wa.me/254708920430" rel="noopener noreferrer"&gt;Chat on WhatsApp&lt;/a&gt;. You can also email me at &lt;a href="mailto:sosmongare@gmail.com"&gt;Here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I'm happy to help you get your integration running smoothly.&lt;/p&gt;

</description>
      <category>api</category>
      <category>mpesa</category>
      <category>daraja</category>
    </item>
    <item>
      <title>Installing and Testing Docker on WSL with MobaXterm on Windows Os</title>
      <dc:creator>Sospeter Mong'are</dc:creator>
      <pubDate>Tue, 16 Jun 2026 10:14:19 +0000</pubDate>
      <link>https://dev.to/msnmongare/installing-and-testing-docker-on-wsl-with-mobaxterm-on-windows-os-ooj</link>
      <guid>https://dev.to/msnmongare/installing-and-testing-docker-on-wsl-with-mobaxterm-on-windows-os-ooj</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;If you are a developer on Windows who wants to use Docker without installing Docker Desktop, you are in the right place. In this guide, you will learn how to install Docker Engine inside WSL (Windows Subsystem for Linux) using MobaXterm, test it with a real Node.js application, and manage your containers like a pro — all from the command line.&lt;/p&gt;

&lt;p&gt;By the end of this article, you will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Understand what WSL and MobaXterm are and why they work great together&lt;/li&gt;
&lt;li&gt;Have Docker Engine installed and running inside Ubuntu on WSL&lt;/li&gt;
&lt;li&gt;Test Docker with the official hello-world image&lt;/li&gt;
&lt;li&gt;Build and run a real Node.js web app inside a Docker container&lt;/li&gt;
&lt;li&gt;Know how to start, stop, and clean up Docker containers&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Who is this guide for?&lt;/strong&gt; Developers who are comfortable with the command line but are new to Docker on WSL. No prior Docker experience required.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  1. What Are WSL and MobaXterm?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  WSL — Windows Subsystem for Linux
&lt;/h3&gt;

&lt;p&gt;WSL is a feature built into Windows 10 and 11 that lets you run a real Linux environment directly on your Windows machine — no virtual machine, no dual boot. You get a full Ubuntu terminal that shares your file system and network with Windows.&lt;/p&gt;

&lt;p&gt;Think of WSL as a thin Linux layer that sits underneath your Windows desktop. It is fast, lightweight, and perfect for running developer tools like Docker, Node.js, Python, and more.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://mobaxterm.mobatek.net/" rel="noopener noreferrer"&gt;MobaXterm — A Better Terminal for WSL&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://mobaxterm.mobatek.net/" rel="noopener noreferrer"&gt;MobaXterm &lt;/a&gt;is a powerful terminal application for Windows that includes built-in SSH, X11 forwarding, and — most importantly — direct access to your WSL sessions. It gives you a much better development experience than the default Windows Terminal for WSL work.&lt;/p&gt;

&lt;p&gt;When you open a WSL tab in MobaXterm, you are working inside your Ubuntu environment. That is where all our Docker commands will run.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Important Note About WSL and systemd:&lt;/strong&gt; By default, WSL does not use systemd (the Linux service manager). This means &lt;code&gt;sudo systemctl start docker&lt;/code&gt; may not work. Instead, we use &lt;code&gt;sudo service docker start&lt;/code&gt;. This guide uses the correct command throughout.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  2. Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before we begin, make sure you have the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Windows 10 (version 2004 or later) or Windows 11&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://learn.microsoft.com/en-us/windows/wsl/install" rel="noopener noreferrer"&gt;WSL 2 installed with Ubuntu&lt;/a&gt;&lt;/strong&gt; (Ubuntu 20.04 or 22.04 recommended)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://mobaxterm.mobatek.net/" rel="noopener noreferrer"&gt;MobaXterm installed&lt;/a&gt;&lt;/strong&gt; — download free from mobaxterm.mobatek.net&lt;/li&gt;
&lt;li&gt;A stable internet connection for downloading packages&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To verify WSL is installed, open PowerShell and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wsl &lt;span class="nt"&gt;--list&lt;/span&gt; &lt;span class="nt"&gt;--verbose&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see your Ubuntu distro listed with Version 2. If not, run &lt;code&gt;wsl --set-version Ubuntu 2&lt;/code&gt; to upgrade.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Opening Your WSL Session in MobaXterm
&lt;/h2&gt;

&lt;p&gt;Launch MobaXterm and start a WSL session:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open MobaXterm from your Windows Start menu&lt;/li&gt;
&lt;li&gt;Click on &lt;strong&gt;Sessions → New Session&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;WSL&lt;/strong&gt; from the session types&lt;/li&gt;
&lt;li&gt;Choose your Ubuntu distribution from the dropdown&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;OK&lt;/strong&gt; — you now have a Linux terminal open!&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Your prompt should look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sospeter@DESKTOP-ABC123:~&lt;span class="err"&gt;$&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All commands in this guide are run inside this MobaXterm WSL terminal.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Installing Docker Engine on WSL
&lt;/h2&gt;

&lt;p&gt;We will install Docker Engine using the official Docker apt repository. Follow each step carefully.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Add Docker's Official GPG Key
&lt;/h3&gt;

&lt;p&gt;First, update your package list and install the required dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;ca-certificates curl &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then create the directory for Docker's GPG key and download 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 install&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt; 0755 &lt;span class="nt"&gt;-d&lt;/span&gt; /etc/apt/keyrings
&lt;span class="nb"&gt;sudo &lt;/span&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://download.docker.com/linux/ubuntu/gpg &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; /etc/apt/keyrings/docker.asc
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;a+r /etc/apt/keyrings/docker.asc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Add the Docker Repository
&lt;/h3&gt;

&lt;p&gt;Now add Docker's official package repository to your Ubuntu apt sources:&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 tee&lt;/span&gt; /etc/apt/sources.list.d/docker.sources &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; /etc/os-release &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;UBUNTU_CODENAME&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;$VERSION_CODENAME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;
Components: stable
Architectures: &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="sh"&gt;
Signed-By: /etc/apt/keyrings/docker.asc
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Install Docker Packages
&lt;/h3&gt;

&lt;p&gt;Install Docker Engine along with the CLI, containerd runtime, and the Compose plugin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;docker-ce docker-ce-cli containerd.io &lt;span class="se"&gt;\&lt;/span&gt;
  docker-buildx-plugin docker-compose-plugin &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This may take a few minutes depending on your internet speed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Start Docker
&lt;/h3&gt;

&lt;p&gt;In WSL, we start Docker using the &lt;code&gt;service&lt;/code&gt; command:&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;service docker start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To verify Docker is running:&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;service docker status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see output that includes &lt;code&gt;Docker is running&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🔁 &lt;strong&gt;Auto-start tip:&lt;/strong&gt; Add &lt;code&gt;sudo service docker start &amp;gt; /dev/null 2&amp;gt;&amp;amp;1&lt;/code&gt; to your &lt;code&gt;~/.bashrc&lt;/code&gt; file so Docker starts automatically every time you open MobaXterm.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Step 5: Add Your User to the Docker Group (Optional but Recommended)
&lt;/h3&gt;

&lt;p&gt;By default, Docker commands require &lt;code&gt;sudo&lt;/code&gt;. To run Docker without 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;usermod &lt;span class="nt"&gt;-aG&lt;/span&gt; docker &lt;span class="nv"&gt;$USER&lt;/span&gt;

&lt;span class="c"&gt;# Apply the group change (or close and reopen your terminal)&lt;/span&gt;
newgrp docker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  5. Verifying Docker with hello-world
&lt;/h2&gt;

&lt;p&gt;Let us confirm Docker is working with the official test image:&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;docker run hello-world
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see output 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;Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from Docker Hub.
 3. The Docker daemon created a new container from that image.
 4. The Docker daemon streamed that output to the Docker client.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you see that — congratulations! 🎉 Docker is installed and working correctly on your WSL.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Building and Running a Real Node.js App
&lt;/h2&gt;

&lt;p&gt;The hello-world image is a good sanity check, but let us go further and run a real web application inside Docker.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Create a Project Folder
&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;mkdir&lt;/span&gt; ~/docker-test &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; ~/docker-test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Create the Application File
&lt;/h3&gt;

&lt;p&gt;Create a file called &lt;code&gt;app.js&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;nano app.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Paste the following code:&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;http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http&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;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeHead&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text/plain&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello from Docker on WSL! 🐳&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Server running on port 3000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save and exit with &lt;code&gt;Ctrl + X&lt;/code&gt;, then &lt;code&gt;Y&lt;/code&gt;, then &lt;code&gt;Enter&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Create the Dockerfile
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nano Dockerfile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Paste the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Use the official Node.js Alpine image (small and fast)&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:18-alpine&lt;/span&gt;

&lt;span class="c"&gt;# Set the working directory inside the container&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="c"&gt;# Copy our application file into the container&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; app.js .&lt;/span&gt;

&lt;span class="c"&gt;# Tell Docker our app listens on port 3000&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;

&lt;span class="c"&gt;# Command to run when the container starts&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", "app.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Build the Docker Image
&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;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; wsl-test-app &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;-t&lt;/code&gt; flag gives the image a name (tag). The &lt;code&gt;.&lt;/code&gt; tells Docker to look for the Dockerfile in the current directory.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Run the Container
&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;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 3000:3000 &lt;span class="nt"&gt;--name&lt;/span&gt; my-test-app wsl-test-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What each flag does:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Flag&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;-d&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run in the background (detached mode)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-p 3000:3000&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Map port 3000 on your host to port 3000 in the container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--name my-test-app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Give the container a friendly name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;wsl-test-app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The image name we just built&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Step 6: Test the Running App
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://localhost:3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hello from Docker on WSL! 🐳
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also open &lt;code&gt;http://localhost:3000&lt;/code&gt; in your Windows browser and see the same result!&lt;/p&gt;




&lt;h2&gt;
  
  
  7. Managing Your Containers
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;What It Does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker ps&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List all running containers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker ps -a&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List all containers including stopped ones&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker logs my-test-app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;View logs from a container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker stop my-test-app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stop a running container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker start my-test-app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start a stopped container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker rm my-test-app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Delete a container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker images&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List all images on your machine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker rmi wsl-test-app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Delete an image&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker exec -it &amp;lt;name&amp;gt; sh&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Open a shell inside a running container&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Cleaning Up Your Test App
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Stop the running container&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker stop my-test-app

&lt;span class="c"&gt;# Remove the container&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker &lt;span class="nb"&gt;rm &lt;/span&gt;my-test-app

&lt;span class="c"&gt;# (Optional) Remove the image&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker rmi wsl-test-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Stopping Docker Engine
&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;service docker stop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  8. Troubleshooting Common Issues
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Cannot connect to the Docker daemon&lt;/strong&gt;&lt;br&gt;
Run &lt;code&gt;sudo service docker start&lt;/code&gt; to start the Docker service.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Permission denied running Docker&lt;/strong&gt;&lt;br&gt;
Either prefix commands with &lt;code&gt;sudo&lt;/code&gt;, or add yourself to the docker group (Section 4, Step 5).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Port already in use&lt;/strong&gt;&lt;br&gt;
Change the port mapping to &lt;code&gt;-p 3001:3000&lt;/code&gt; and visit &lt;code&gt;localhost:3001&lt;/code&gt; instead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WSL using too much RAM&lt;/strong&gt;&lt;br&gt;
Create &lt;code&gt;C:\Users\YourName\.wslconfig&lt;/code&gt; with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[wsl2]&lt;/span&gt;
&lt;span class="py"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;4GB&lt;/span&gt;
&lt;span class="py"&gt;processors&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  9. Quick Reference Cheat Sheet
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Docker service&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;service docker start        &lt;span class="c"&gt;# Start Docker&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;service docker stop         &lt;span class="c"&gt;# Stop Docker&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;service docker status       &lt;span class="c"&gt;# Check status&lt;/span&gt;

&lt;span class="c"&gt;# Images&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; name &lt;span class="nb"&gt;.&lt;/span&gt;      &lt;span class="c"&gt;# Build image from Dockerfile&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker images               &lt;span class="c"&gt;# List images&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker rmi image-name       &lt;span class="c"&gt;# Delete image&lt;/span&gt;

&lt;span class="c"&gt;# Containers&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 3000:3000 &lt;span class="nt"&gt;--name&lt;/span&gt; app image   &lt;span class="c"&gt;# Run container&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker ps                   &lt;span class="c"&gt;# See running containers&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker ps &lt;span class="nt"&gt;-a&lt;/span&gt;                &lt;span class="c"&gt;# See all containers&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker logs app             &lt;span class="c"&gt;# View logs&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker stop app             &lt;span class="c"&gt;# Stop container&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker &lt;span class="nb"&gt;rm &lt;/span&gt;app               &lt;span class="c"&gt;# Remove container&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; app sh      &lt;span class="c"&gt;# Shell into container&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






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

&lt;p&gt;You have successfully:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Installed Docker Engine on WSL using the official apt repository&lt;/li&gt;
&lt;li&gt;✅ Started and verified Docker is running with the hello-world image&lt;/li&gt;
&lt;li&gt;✅ Built and deployed a real Node.js application inside a Docker container&lt;/li&gt;
&lt;li&gt;✅ Learned how to manage, stop, and clean up containers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Docker on WSL is a powerful setup that gives you all the benefits of Linux containers without leaving your Windows machine. As you grow more comfortable, explore Docker Compose for multi-container applications, volume mounts for persistent data, and container networking for microservices architectures.&lt;/p&gt;

&lt;p&gt;Happy coding and containerizing! 🐳&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Found this helpful? Share it with fellow developers. Follow me on &lt;a href="https://x.com/msnmongare" rel="noopener noreferrer"&gt;X&lt;/a&gt; for more backend, API, and DevOps content.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ubuntu</category>
      <category>linux</category>
      <category>tutorial</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How to Install dbt with SQL Server on WSL (Windows Subsystem for Linux)</title>
      <dc:creator>Sospeter Mong'are</dc:creator>
      <pubDate>Wed, 10 Jun 2026 13:31:41 +0000</pubDate>
      <link>https://dev.to/msnmongare/how-to-install-dbt-with-sql-server-on-wsl-windows-subsystem-for-linux-2m3e</link>
      <guid>https://dev.to/msnmongare/how-to-install-dbt-with-sql-server-on-wsl-windows-subsystem-for-linux-2m3e</guid>
      <description>&lt;p&gt;Note: &lt;em&gt;A beginner-friendly, step-by-step guide for data engineers on Windows&lt;/em&gt;&lt;/p&gt;




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

&lt;p&gt;dbt (data build tool) is an open-source transformation tool that lets you write SQL-based data models, test them, and document them — all from the command line. It sits on top of your database and transforms raw data into analytics-ready tables and views.&lt;/p&gt;

&lt;p&gt;If you're working with data pipelines, ELT workflows, or data warehouses, dbt is a tool you'll encounter regularly.&lt;/p&gt;




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

&lt;p&gt;Before starting, make sure you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;WSL 2 with Ubuntu 24.04&lt;/strong&gt; installed on your Windows machine&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Python 3.12&lt;/strong&gt; available (&lt;code&gt;python3 --version&lt;/code&gt; to check)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A virtual environment&lt;/strong&gt; already created and activated (see the Airflow installation guide if you need to set one up)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Access to a SQL Server instance&lt;/strong&gt; — remote, Azure SQL, or local&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 1: Activate Your Virtual Environment
&lt;/h2&gt;

&lt;p&gt;Always work inside a virtual environment to keep your dependencies isolated:&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;source&lt;/span&gt; ~/your-project-folder/env/bin/activate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your prompt should show &lt;code&gt;(env)&lt;/code&gt; at the beginning confirming it's active.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: Install dbt with the SQL Server Adapter
&lt;/h2&gt;

&lt;p&gt;The SQL Server adapter for dbt is maintained as &lt;code&gt;dbt-sqlserver&lt;/code&gt;. Installing it also pulls in &lt;code&gt;dbt-core&lt;/code&gt; automatically:&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;dbt-sqlserver
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify the installation:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Confirm the adapter is installed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip show dbt-sqlserver
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see:&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;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dbt-sqlserver&lt;/span&gt;
&lt;span class="na"&gt;Version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1.x.x&lt;/span&gt;
&lt;span class="na"&gt;Summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;A Microsoft SQL Server adapter plugin for dbt&lt;/span&gt;
&lt;span class="na"&gt;Requires&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dbt-adapters, dbt-common, dbt-core, pyodbc&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;code&gt;dbt --version&lt;/code&gt; may not always list plugins explicitly in some environments — &lt;code&gt;pip show dbt-sqlserver&lt;/code&gt; is the reliable way to confirm the adapter is present.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 3: Install the Microsoft ODBC Driver for SQL Server
&lt;/h2&gt;

&lt;p&gt;dbt-sqlserver uses &lt;code&gt;pyodbc&lt;/code&gt; under the hood, which requires Microsoft's ODBC driver to physically connect to SQL Server. Run the following commands &lt;strong&gt;one at a time&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Command 1 — Download and register Microsoft's GPG signing key:&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;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://packages.microsoft.com/keys/microsoft.asc | &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; /usr/share/keyrings/microsoft-prod.gpg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Command 2 — Add Microsoft's package repository:&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;curl https://packages.microsoft.com/config/ubuntu/24.04/prod.list | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; /etc/apt/sources.list.d/mssql-release.list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Command 3 — Refresh apt to pick up the new repository:&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="nb"&gt;sudo &lt;/span&gt;apt update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Command 4 — Install the ODBC driver:&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="nb"&gt;sudo &lt;/span&gt;&lt;span class="nv"&gt;ACCEPT_EULA&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Y apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; msodbcsql18 unixodbc-dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Verify the driver is registered:&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;odbcinst &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"ODBC Driver 18 for SQL Server"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[ODBC Driver 18 for SQL Server]&lt;/span&gt;
&lt;span class="py"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Microsoft ODBC Driver 18 for SQL Server&lt;/span&gt;
&lt;span class="py"&gt;Driver&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/opt/microsoft/msodbcsql18/lib64/libmsodbcsql-18.6.so.2.1&lt;/span&gt;
&lt;span class="py"&gt;UsageCount&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This confirms the driver is installed and ready.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4: Clone Your dbt Project Repository
&lt;/h2&gt;

&lt;p&gt;If you have an existing dbt project in a Git repository, clone 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;cd&lt;/span&gt; ~
git clone &amp;lt;YOUR_REPO_URL_HERE&amp;gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; &amp;lt;cloned-folder-name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the repository is private, authenticate using a GitHub Personal Access Token (PAT):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://&amp;lt;your-github-username&amp;gt;:&amp;lt;your-PAT&amp;gt;@github.com/org/repo.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 5: Install dbt Project Dependencies
&lt;/h2&gt;

&lt;p&gt;Most dbt projects have a &lt;code&gt;packages.yml&lt;/code&gt; file that lists external dbt packages. Install them with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dbt deps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 6: Configure Your &lt;code&gt;profiles.yml&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;dbt connects to your database using a &lt;code&gt;profiles.yml&lt;/code&gt; file. By default it lives at &lt;code&gt;~/.dbt/profiles.yml&lt;/code&gt;, but some projects include it locally inside the repo folder.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Create the default profiles directory and file:&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="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/.dbt
nano ~/.dbt/profiles.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Paste the following template and fill in your actual credentials:&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;your_project_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dev&lt;/span&gt;
  &lt;span class="na"&gt;outputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;dev&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sqlserver&lt;/span&gt;
      &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ODBC&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Driver&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;18&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;for&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;SQL&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Server"&lt;/span&gt;
      &lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;your_server_address&lt;/span&gt;        &lt;span class="c1"&gt;# e.g. 192.168.1.10 or myserver.database.windows.net&lt;/span&gt;
      &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1433&lt;/span&gt;
      &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;your_database_name&lt;/span&gt;
      &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;your_schema_name&lt;/span&gt;
      &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;your_username&lt;/span&gt;
      &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;your_password&lt;/span&gt;
      &lt;span class="na"&gt;encrypt&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;trust_cert&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;                   &lt;span class="c1"&gt;# set to false in production with a valid SSL cert&lt;/span&gt;
      &lt;span class="na"&gt;login_timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt;                  &lt;span class="c1"&gt;# seconds before connection attempt times out&lt;/span&gt;

    &lt;span class="na"&gt;prod&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sqlserver&lt;/span&gt;
      &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ODBC&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Driver&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;18&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;for&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;SQL&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Server"&lt;/span&gt;
      &lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;your_prod_server_address&lt;/span&gt;
      &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1433&lt;/span&gt;
      &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;your_prod_database&lt;/span&gt;
      &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;your_prod_schema&lt;/span&gt;
      &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;your_prod_username&lt;/span&gt;
      &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;your_prod_password&lt;/span&gt;
      &lt;span class="na"&gt;encrypt&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;trust_cert&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;login_timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; The top-level key (&lt;code&gt;your_project_name&lt;/code&gt;) must exactly match the &lt;code&gt;name:&lt;/code&gt; field inside the project's &lt;code&gt;dbt_project.yml&lt;/code&gt; file.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Save and exit (&lt;code&gt;Ctrl+O&lt;/code&gt;, &lt;code&gt;Enter&lt;/code&gt;, &lt;code&gt;Ctrl+X&lt;/code&gt;).&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 7: Test the Connection
&lt;/h2&gt;

&lt;p&gt;Run dbt's built-in connection test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dbt debug &lt;span class="nt"&gt;--target&lt;/span&gt; dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A successful output looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;All checks passed!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you see a &lt;code&gt;Login timeout expired&lt;/code&gt; error, check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your server address is correct and reachable&lt;/li&gt;
&lt;li&gt;Port 1433 is open on the server's firewall&lt;/li&gt;
&lt;li&gt;Your credentials are correct in &lt;code&gt;profiles.yml&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;You're on the right network (VPN may be required for remote servers)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Test if the server port is reachable from WSL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nc &lt;span class="nt"&gt;-zv&lt;/span&gt; your_server_address 1433
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 8: Run Your dbt Models
&lt;/h2&gt;

&lt;p&gt;Once the connection test passes, run a specific model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dbt run &lt;span class="nt"&gt;--select&lt;/span&gt; model_name &lt;span class="nt"&gt;--target&lt;/span&gt; dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run all models:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dbt run &lt;span class="nt"&gt;--target&lt;/span&gt; dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run models against production:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dbt run &lt;span class="nt"&gt;--select&lt;/span&gt; model_name &lt;span class="nt"&gt;--target&lt;/span&gt; prod
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;List all available models in the project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dbt &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;--target&lt;/span&gt; dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Other Database Adapters
&lt;/h2&gt;

&lt;p&gt;dbt supports many databases. Here are the install commands for the most common ones:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Database&lt;/th&gt;
&lt;th&gt;Install Command&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SQL Server / Azure SQL&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pip install dbt-sqlserver&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PostgreSQL&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pip install dbt-postgres&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BigQuery&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pip install dbt-bigquery&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Snowflake&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pip install dbt-snowflake&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MySQL&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pip install dbt-mysql&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Redshift&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pip install dbt-redshift&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DuckDB&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pip install dbt-duckdb&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Databricks&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pip install dbt-databricks&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Trino&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pip install dbt-trino&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Each adapter has its own &lt;code&gt;profiles.yml&lt;/code&gt; format. The &lt;code&gt;type:&lt;/code&gt; field changes per adapter. For example:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PostgreSQL:&lt;/strong&gt;&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;my_project&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dev&lt;/span&gt;
  &lt;span class="na"&gt;outputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;dev&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
      &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;localhost&lt;/span&gt;
      &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5432&lt;/span&gt;
      &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my_database&lt;/span&gt;
      &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;public&lt;/span&gt;
      &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my_user&lt;/span&gt;
      &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my_password&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;BigQuery:&lt;/strong&gt;&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;my_project&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dev&lt;/span&gt;
  &lt;span class="na"&gt;outputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;dev&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bigquery&lt;/span&gt;
      &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;service-account&lt;/span&gt;
      &lt;span class="na"&gt;project&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my_gcp_project&lt;/span&gt;
      &lt;span class="na"&gt;dataset&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my_dataset&lt;/span&gt;
      &lt;span class="na"&gt;keyfile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/path/to/service-account.json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Snowflake:&lt;/strong&gt;&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;my_project&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dev&lt;/span&gt;
  &lt;span class="na"&gt;outputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;dev&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;snowflake&lt;/span&gt;
      &lt;span class="na"&gt;account&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my_account&lt;/span&gt;
      &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my_user&lt;/span&gt;
      &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my_password&lt;/span&gt;
      &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my_database&lt;/span&gt;
      &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my_schema&lt;/span&gt;
      &lt;span class="na"&gt;warehouse&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my_warehouse&lt;/span&gt;
      &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my_role&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Quick Reference: Most Used dbt Commands
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dbt debug              &lt;span class="c"&gt;# Test database connection&lt;/span&gt;
dbt deps               &lt;span class="c"&gt;# Install project package dependencies&lt;/span&gt;
dbt run                &lt;span class="c"&gt;# Run all models&lt;/span&gt;
dbt run &lt;span class="nt"&gt;--select&lt;/span&gt; model_name         &lt;span class="c"&gt;# Run a specific model&lt;/span&gt;
dbt run &lt;span class="nt"&gt;--target&lt;/span&gt; prod               &lt;span class="c"&gt;# Run against a specific target&lt;/span&gt;
dbt &lt;span class="nb"&gt;test&lt;/span&gt;                            &lt;span class="c"&gt;# Run data tests&lt;/span&gt;
dbt &lt;span class="nb"&gt;ls&lt;/span&gt;                              &lt;span class="c"&gt;# List all models&lt;/span&gt;
dbt compile                         &lt;span class="c"&gt;# Compile SQL without running&lt;/span&gt;
dbt docs generate &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; dbt docs serve &lt;span class="c"&gt;# Generate and view documentation&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Summary of Installation Commands
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Activate virtual environment&lt;/span&gt;
&lt;span class="nb"&gt;source&lt;/span&gt; ~/your-project/env/bin/activate

&lt;span class="c"&gt;# Install dbt with SQL Server adapter&lt;/span&gt;
pip &lt;span class="nb"&gt;install &lt;/span&gt;dbt-sqlserver

&lt;span class="c"&gt;# Install ODBC Driver (run one at a time)&lt;/span&gt;
curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://packages.microsoft.com/keys/microsoft.asc | &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; /usr/share/keyrings/microsoft-prod.gpg
curl https://packages.microsoft.com/config/ubuntu/24.04/prod.list | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; /etc/apt/sources.list.d/mssql-release.list
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update
&lt;span class="nb"&gt;sudo &lt;/span&gt;&lt;span class="nv"&gt;ACCEPT_EULA&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Y apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; msodbcsql18 unixodbc-dev

&lt;span class="c"&gt;# Verify ODBC driver&lt;/span&gt;
odbcinst &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"ODBC Driver 18 for SQL Server"&lt;/span&gt;

&lt;span class="c"&gt;# Clone project repo&lt;/span&gt;
git clone &amp;lt;REPO_URL&amp;gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; &amp;lt;project-folder&amp;gt;

&lt;span class="c"&gt;# Install dbt packages&lt;/span&gt;
dbt deps

&lt;span class="c"&gt;# Configure connection&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/.dbt &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; nano ~/.dbt/profiles.yml

&lt;span class="c"&gt;# Test connection&lt;/span&gt;
dbt debug &lt;span class="nt"&gt;--target&lt;/span&gt; dev

&lt;span class="c"&gt;# Run models&lt;/span&gt;
dbt run &lt;span class="nt"&gt;--target&lt;/span&gt; dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;em&gt;Written based on a real dbt + SQL Server setup on WSL2 Ubuntu 24.04 (Noble) with ODBC Driver 18.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>database</category>
      <category>data</category>
      <category>science</category>
    </item>
    <item>
      <title>How to Install Apache Airflow 2.9.1 on WSL (Windows Subsystem for Linux)</title>
      <dc:creator>Sospeter Mong'are</dc:creator>
      <pubDate>Tue, 09 Jun 2026 13:48:58 +0000</pubDate>
      <link>https://dev.to/msnmongare/how-to-install-apache-airflow-291-on-wsl-windows-subsystem-for-linux-2e1j</link>
      <guid>https://dev.to/msnmongare/how-to-install-apache-airflow-291-on-wsl-windows-subsystem-for-linux-2e1j</guid>
      <description>&lt;p&gt;Note: &lt;em&gt;A beginner-friendly, step-by-step guide for developers on Windows&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What is Apache Airflow?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://airflow.apache.org/" rel="noopener noreferrer"&gt;Apache Airflow&lt;/a&gt; is an open-source platform used to programmatically author, schedule, and monitor workflows. It's widely used in data engineering, MLOps, and backend automation pipelines. If you're building ETL pipelines, running scheduled tasks, or orchestrating complex workflows, Airflow is a tool you'll likely encounter.&lt;/p&gt;




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

&lt;p&gt;Before we begin, make sure you have the following in place:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Windows Subsystem for Linux (WSL) installed
&lt;/h3&gt;

&lt;p&gt;You need WSL 2 with &lt;strong&gt;Ubuntu 24.04 (Noble)&lt;/strong&gt; installed on your Windows machine. If you haven't set it up yet, open PowerShell as Administrator and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;wsl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--install&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restart your machine when prompted, then launch &lt;strong&gt;Ubuntu&lt;/strong&gt; from the Start menu.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. &lt;a href="https://mobaxterm.mobatek.net/" rel="noopener noreferrer"&gt;MobiXterm&lt;/a&gt; or Windows Terminal (optional but recommended)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://mobaxterm.mobatek.net/" rel="noopener noreferrer"&gt;MobiXterm &lt;/a&gt;or Windows Terminal gives you a better terminal experience for working inside WSL. Either works fine.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Basic familiarity with the Linux terminal
&lt;/h3&gt;

&lt;p&gt;You don't need to be a Linux expert, but knowing how to navigate directories (&lt;code&gt;cd&lt;/code&gt;, &lt;code&gt;ls&lt;/code&gt;) and run commands will help.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: Update Your System
&lt;/h2&gt;

&lt;p&gt;Open your WSL terminal and run:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;This ensures your package lists and installed packages are up to date.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: Install Python 3 and Check the Version
&lt;/h2&gt;

&lt;p&gt;Ubuntu 24.04 ships with Python 3.12 by default. Verify it's available:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;You should see something like &lt;code&gt;Python 3.12.x&lt;/code&gt;. If not, install 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;apt &lt;span class="nb"&gt;install &lt;/span&gt;python3 python3-dev &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 3: Install &lt;code&gt;python3.12-venv&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Airflow should always be installed inside a virtual environment to avoid dependency conflicts with your system Python.&lt;/p&gt;

&lt;p&gt;First, install the venv module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;python3.12-venv &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Troubleshooting - Network/download errors?&lt;/strong&gt;&lt;br&gt;
On some networks, &lt;code&gt;apt&lt;/code&gt; may time out trying to download packages. If you see a &lt;code&gt;Connection timed out&lt;/code&gt; or &lt;code&gt;Connection failed&lt;/code&gt; error for &lt;code&gt;python3-pip-whl&lt;/code&gt;, use &lt;code&gt;wget&lt;/code&gt; to download it manually:&lt;/p&gt;


&lt;pre class="highlight shell"&gt;&lt;code&gt;wget &lt;span class="nt"&gt;-O&lt;/span&gt; python3-pip-whl.deb &lt;span class="s2"&gt;"https://launchpadlibrarian.net/753358547/python3-pip-whl_24.0+dfsg-1ubuntu1.3_all.deb"&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;dpkg &lt;span class="nt"&gt;-i&lt;/span&gt; python3-pip-whl.deb
&lt;span class="nb"&gt;sudo &lt;/span&gt;dpkg &lt;span class="nt"&gt;--configure&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nt"&gt;--fix-broken&lt;/span&gt; &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Then retry the venv install:&lt;/p&gt;


&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;python3.12-venv &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 4: Create a Project Directory and Virtual Environment
&lt;/h2&gt;

&lt;p&gt;Create a dedicated folder for your Airflow installation:&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;mkdir&lt;/span&gt; ~/airflow-2.9.1
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/airflow-2.9.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now create a virtual environment inside it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; venv venv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Activate the virtual environment:&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;source &lt;/span&gt;venv/bin/activate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your terminal prompt should now show &lt;code&gt;(venv)&lt;/code&gt; at the beginning, like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;(venv) user@DESKTOP-XXXX:~/airflow-2.9.1$&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This confirms you are inside the virtual environment.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5: Upgrade pip
&lt;/h2&gt;

&lt;p&gt;Before installing Airflow, upgrade pip to the latest version:&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; &lt;span class="nt"&gt;--upgrade&lt;/span&gt; pip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 6: Install Apache Airflow 2.9.1
&lt;/h2&gt;

&lt;p&gt;Install Airflow using the official constraints file. This is &lt;strong&gt;important&lt;/strong&gt; - Airflow has many dependencies, and the constraints file ensures compatible versions are installed together without conflicts.&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;apache-airflow&lt;span class="o"&gt;==&lt;/span&gt;2.9.1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--constraint&lt;/span&gt; &lt;span class="s2"&gt;"https://raw.githubusercontent.com/apache/airflow/constraints-2.9.1/constraints-3.12.txt"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This may take a few minutes. Once complete, verify the installation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;airflow version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 7: Initialize the Airflow Database
&lt;/h2&gt;

&lt;p&gt;Airflow uses a metadata database to track DAG runs, tasks, logs, and more. Initialize it with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;airflow db init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates the default SQLite database and the &lt;code&gt;~/airflow&lt;/code&gt; directory with all necessary config files.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 8: Create an Admin User
&lt;/h2&gt;

&lt;p&gt;Create your first admin user to log into the Airflow web UI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;airflow &lt;span class="nb"&gt;users &lt;/span&gt;create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--username&lt;/span&gt; admin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--firstname&lt;/span&gt; YourFirstName &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--lastname&lt;/span&gt; YourLastName &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--role&lt;/span&gt; Admin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--email&lt;/span&gt; your@email.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will be prompted to set a password. Enter and confirm it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 9: Start the Airflow Web Server and Scheduler
&lt;/h2&gt;

&lt;p&gt;Airflow requires two processes running simultaneously - the &lt;strong&gt;webserver&lt;/strong&gt; and the &lt;strong&gt;scheduler&lt;/strong&gt;. Open two separate WSL terminal tabs or windows for this.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Terminal 1 - Start the webserver:&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="nb"&gt;cd&lt;/span&gt; ~/airflow-2.9.1
&lt;span class="nb"&gt;source &lt;/span&gt;venv/bin/activate
airflow webserver &lt;span class="nt"&gt;--port&lt;/span&gt; 8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Terminal 2 - Start the scheduler:&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="nb"&gt;cd&lt;/span&gt; ~/airflow-2.9.1
&lt;span class="nb"&gt;source &lt;/span&gt;venv/bin/activate
airflow scheduler
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 10: Access the Airflow UI
&lt;/h2&gt;

&lt;p&gt;Open your browser and navigate 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://localhost:8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will see the Airflow login page. Enter the username and password you created in Step 8.&lt;/p&gt;

&lt;p&gt;You should now be inside the &lt;strong&gt;Airflow Dashboard&lt;/strong&gt; - ready to create and manage your DAGs!&lt;/p&gt;




&lt;h2&gt;
  
  
  Quick Reference: Starting Airflow After a Reboot
&lt;/h2&gt;

&lt;p&gt;Every time you restart your machine and want to use Airflow, run these commands in two terminals:&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;# Both terminals: navigate and activate venv&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/airflow-2.9.1
&lt;span class="nb"&gt;source &lt;/span&gt;venv/bin/activate

&lt;span class="c"&gt;# Terminal 1&lt;/span&gt;
airflow webserver &lt;span class="nt"&gt;--port&lt;/span&gt; 8080

&lt;span class="c"&gt;# Terminal 2&lt;/span&gt;
airflow scheduler
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Summary of All Commands
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Update system&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt upgrade &lt;span class="nt"&gt;-y&lt;/span&gt;

&lt;span class="c"&gt;# Install Python venv&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;python3.12-venv &lt;span class="nt"&gt;-y&lt;/span&gt;

&lt;span class="c"&gt;# Create project directory and venv&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; ~/airflow-2.9.1 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; ~/airflow-2.9.1
python3 &lt;span class="nt"&gt;-m&lt;/span&gt; venv venv
&lt;span class="nb"&gt;source &lt;/span&gt;venv/bin/activate

&lt;span class="c"&gt;# Install Airflow&lt;/span&gt;
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--upgrade&lt;/span&gt; pip
pip &lt;span class="nb"&gt;install &lt;/span&gt;apache-airflow&lt;span class="o"&gt;==&lt;/span&gt;2.9.1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--constraint&lt;/span&gt; &lt;span class="s2"&gt;"https://raw.githubusercontent.com/apache/airflow/constraints-2.9.1/constraints-3.12.txt"&lt;/span&gt;

&lt;span class="c"&gt;# Verify&lt;/span&gt;
airflow version

&lt;span class="c"&gt;# Initialize DB and create user&lt;/span&gt;
airflow db init
airflow &lt;span class="nb"&gt;users &lt;/span&gt;create &lt;span class="nt"&gt;--username&lt;/span&gt; admin &lt;span class="nt"&gt;--firstname&lt;/span&gt; YourFirstName &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--lastname&lt;/span&gt; YourLastName &lt;span class="nt"&gt;--role&lt;/span&gt; Admin &lt;span class="nt"&gt;--email&lt;/span&gt; your@email.com

&lt;span class="c"&gt;# Start services&lt;/span&gt;
airflow webserver &lt;span class="nt"&gt;--port&lt;/span&gt; 8080   &lt;span class="c"&gt;# Terminal 1&lt;/span&gt;
airflow scheduler               &lt;span class="c"&gt;# Terminal 2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;Now that Airflow is running, here are some things to explore next:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Write your first DAG&lt;/strong&gt; - create a Python file in &lt;code&gt;~/airflow/dags/&lt;/code&gt; to define a workflow&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Explore Airflow Operators&lt;/strong&gt; - BashOperator, PythonOperator, and more&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set up PostgreSQL&lt;/strong&gt; as the Airflow backend (recommended for production over SQLite)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use the CeleryExecutor&lt;/strong&gt; for parallel task execution&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>linux</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
