<?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>Stop Giving AI Coding Agents Your Passwords: A Practical Guide to Secure Credentials</title>
      <dc:creator>Sospeter Mong'are</dc:creator>
      <pubDate>Fri, 25 Sep 2026 19:31:56 +0000</pubDate>
      <link>https://dev.to/msnmongare/stop-giving-ai-coding-agents-your-passwords-a-practical-guide-to-secure-credentials-59ee</link>
      <guid>https://dev.to/msnmongare/stop-giving-ai-coding-agents-your-passwords-a-practical-guide-to-secure-credentials-59ee</guid>
      <description>&lt;p&gt;AI coding agents such as Cursor, Claude Code, GitHub Copilot and other agentic development tools are changing how developers build and deploy software.&lt;/p&gt;

&lt;p&gt;They can read your code, modify files, run terminal commands, execute tests and, depending on how they are configured, interact with your servers and deployment infrastructure.&lt;/p&gt;

&lt;p&gt;That power is useful - but it also introduces a security problem:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do you give an AI coding agent enough access to deploy and troubleshoot your application without giving it the keys to your entire infrastructure?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The answer is not simply "put your password in a &lt;code&gt;.env&lt;/code&gt; file."&lt;/p&gt;

&lt;p&gt;The better approach is to use &lt;strong&gt;least privilege, scoped credentials, SSH keys, CI/CD secrets and separate deployment accounts&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Here is a practical approach.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Never Paste Your VPS or cPanel Password Into an AI Chat
&lt;/h2&gt;

&lt;p&gt;This should be your first rule.&lt;/p&gt;

&lt;p&gt;Avoid prompts like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;My VPS password is MySuperSecretPassword123.

SSH into the server and deploy my application.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cPanel username: myuser
cPanel password: MyPassword123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even if you trust the AI tool, you are unnecessarily exposing a credential through a conversation.&lt;/p&gt;

&lt;p&gt;That credential could potentially appear in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Chat history&lt;/li&gt;
&lt;li&gt;Terminal output&lt;/li&gt;
&lt;li&gt;Agent logs&lt;/li&gt;
&lt;li&gt;Debug output&lt;/li&gt;
&lt;li&gt;Screenshots&lt;/li&gt;
&lt;li&gt;Project files&lt;/li&gt;
&lt;li&gt;Shell history&lt;/li&gt;
&lt;li&gt;Deployment logs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;More importantly, you are giving the AI agent a secret that may provide far more access than the task actually requires.&lt;/p&gt;

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

&lt;blockquote&gt;
&lt;p&gt;"How do I give Cursor my server password?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"How do I let Cursor perform this task with the minimum access required?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That change in mindset is the foundation of secure AI-assisted development.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Use Environment Variables - But Understand Their Limitation
&lt;/h2&gt;

&lt;p&gt;For local development and deployment scripts, environment variables are much better than hardcoding credentials into your application.&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;CPANEL_FTP_HOST=ftp.example.com
CPANEL_FTP_USER=my_deployment_user
CPANEL_FTP_PASS=your_password
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your deployment script can then read these values.&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 python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="n"&gt;host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CPANEL_FTP_HOST&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CPANEL_FTP_USER&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CPANEL_FTP_PASS&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;And make sure &lt;code&gt;.env&lt;/code&gt; is ignored by Git:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.env
.env.*
!.env.example
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can safely commit a template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CPANEL_FTP_HOST=
CPANEL_FTP_USER=
CPANEL_FTP_PASS=
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  But here is the important part
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;.env&lt;/code&gt; protects your secrets from being committed to Git. It does not necessarily protect them from an AI coding agent.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If Cursor can read your project directory, it may be able to read &lt;code&gt;.env&lt;/code&gt; too.&lt;/p&gt;

&lt;p&gt;Therefore, don't think:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"It is in &lt;code&gt;.env&lt;/code&gt;, so the AI cannot see it."&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;blockquote&gt;
&lt;p&gt;"The secret isn't in my source code or Git history, but I still need to control who and what can access the environment."&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  3. Prefer SSH Keys Over Password Authentication
&lt;/h2&gt;

&lt;p&gt;For VPS access, SSH keys are generally preferable to passwords.&lt;/p&gt;

&lt;p&gt;Generate an Ed25519 key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh-keygen &lt;span class="nt"&gt;-t&lt;/span&gt; ed25519
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will typically have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/.ssh/id_ed25519
~/.ssh/id_ed25519.pub
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The public key goes to the server.&lt;/p&gt;

&lt;p&gt;The private key stays on your machine.&lt;/p&gt;

&lt;p&gt;Never share the private key:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;The public key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;id_ed25519.pub
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;is the one intended to be installed on the server.&lt;/p&gt;

&lt;p&gt;You can then connect using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh deploy@your-server.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This eliminates the need to paste a server password into an AI conversation.&lt;/p&gt;

&lt;h3&gt;
  
  
  But there is another important issue
&lt;/h3&gt;

&lt;p&gt;An SSH key is only as safe as the account behind it.&lt;/p&gt;

&lt;p&gt;If your key connects as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;root@production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;you have potentially given the AI agent full control over the server.&lt;/p&gt;

&lt;p&gt;That is not much better than giving it the root password.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Create a Dedicated Deployment User
&lt;/h2&gt;

&lt;p&gt;Instead of giving an AI agent root access, create a dedicated user:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Give that user access only to what it needs.&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;/var/www/myapp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;rather than the entire server.&lt;/p&gt;

&lt;p&gt;Your architecture becomes:&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
   |
   v
SSH Key
   |
   v
deploy user
   |
   v
/var/www/myapp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of:&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
   |
   v
root credentials
   |
   v
Entire VPS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the principle of &lt;strong&gt;least privilege&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If the deployment credential is compromised, the potential damage is limited.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. For cPanel, Create a Dedicated Deployment Account
&lt;/h2&gt;

&lt;p&gt;The same principle applies to cPanel.&lt;/p&gt;

&lt;p&gt;Your main cPanel account may have access to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Websites&lt;/li&gt;
&lt;li&gt;Databases&lt;/li&gt;
&lt;li&gt;Email&lt;/li&gt;
&lt;li&gt;DNS&lt;/li&gt;
&lt;li&gt;FTP&lt;/li&gt;
&lt;li&gt;SSL&lt;/li&gt;
&lt;li&gt;Cron jobs&lt;/li&gt;
&lt;li&gt;Backups&lt;/li&gt;
&lt;li&gt;File Manager&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is usually no reason for a deployment script to have access to everything.&lt;/p&gt;

&lt;p&gt;Instead, create a dedicated FTP/SFTP account for the application.&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;Username:
myapp-deploy

Directory:
/public_html/my-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the deployment credential is associated with the application rather than your entire hosting account.&lt;/p&gt;

&lt;p&gt;If the credential is compromised, the attacker does not automatically get your entire cPanel account.&lt;/p&gt;

&lt;p&gt;Where supported, prefer secure protocols such as &lt;strong&gt;SFTP or SSH&lt;/strong&gt; over plain FTP.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Separate Development, Staging and Production Credentials
&lt;/h2&gt;

&lt;p&gt;Don't use the same credentials everywhere.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Development -&amp;gt; Production credentials
Staging     -&amp;gt; Production credentials
Production  -&amp;gt; Production credentials
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Development
   |
   +-- Development credentials

Staging
   |
   +-- Staging credentials

Production
   |
   +-- Production credentials
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This becomes especially important when working with AI agents.&lt;/p&gt;

&lt;p&gt;Give your development environment access to test credentials whenever possible.&lt;/p&gt;

&lt;p&gt;For example, if you are integrating a payment API, use sandbox credentials during development instead of your live production secret.&lt;/p&gt;

&lt;p&gt;The same principle applies to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Databases&lt;/li&gt;
&lt;li&gt;Cloud accounts&lt;/li&gt;
&lt;li&gt;SSH&lt;/li&gt;
&lt;li&gt;Payment APIs&lt;/li&gt;
&lt;li&gt;Email providers&lt;/li&gt;
&lt;li&gt;Third-party APIs&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  7. Keep Production Secrets Out of the AI Agent Where Possible
&lt;/h2&gt;

&lt;p&gt;For production deployments, a better architecture is to let the AI agent work on the code while your CI/CD system handles the production credentials.&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;Cursor
   |
   | git push
   v
GitHub
   |
   v
CI/CD
   |
   | Deployment secrets
   v
Production VPS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your GitHub repository contains the code and deployment workflow, but not the actual production password.&lt;/p&gt;

&lt;p&gt;Secrets can be stored in your CI/CD platform's secret store.&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;DEPLOY_HOST
DEPLOY_USER
DEPLOY_SSH_KEY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The workflow uses those values during deployment without putting them directly into the repository.&lt;/p&gt;

&lt;p&gt;This creates a useful separation:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI agent -&amp;gt; code&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CI/CD -&amp;gt; production deployment&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That is often safer than allowing the AI agent to directly administer production.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. Give AI Agents the Minimum Access They Need
&lt;/h2&gt;

&lt;p&gt;Before giving an AI agent access to anything, ask:&lt;/p&gt;

&lt;h3&gt;
  
  
  What does it need to do?
&lt;/h3&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;Deploy my application.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What does it need to access?
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/var/www/myapp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What permissions does it need?
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Read + write application files
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  How long does it need access?
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Only during deployment
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then create credentials around those requirements.&lt;/p&gt;

&lt;p&gt;Don't start with:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Here is my root password. Figure it out."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Start with:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"This deployment user can only modify this application's directory."&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  9. Watch Your Terminal Output
&lt;/h2&gt;

&lt;p&gt;Credentials can leak even when you don't explicitly paste them into a chat.&lt;/p&gt;

&lt;p&gt;Avoid commands such as:&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="nv"&gt;$CPANEL_PASSWORD&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or:&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;env&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;if your environment contains secrets.&lt;/p&gt;

&lt;p&gt;Also be careful with shell debugging such as:&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;set&lt;/span&gt; &lt;span class="nt"&gt;-x&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;because commands containing sensitive values may appear in terminal or CI/CD logs.&lt;/p&gt;

&lt;p&gt;Your rule should be:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Secrets should not appear in source code, terminal output, application logs or CI/CD logs.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  10. What If You Already Shared a Credential?
&lt;/h2&gt;

&lt;p&gt;If you have already pasted a production password, API key or private key into an AI conversation, don't simply delete the message and assume the problem is solved.&lt;/p&gt;

&lt;p&gt;Treat the credential as potentially exposed.&lt;/p&gt;

&lt;p&gt;Rotate it.&lt;/p&gt;

&lt;p&gt;A simple response procedure is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Identify the exposed credential
2. Revoke it
3. Generate a replacement
4. Update the application or deployment system
5. Review authentication and server logs
6. Check for suspicious activity
7. Review how the credential was exposed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For an SSH key, generate a replacement key and remove the old public key from the server.&lt;/p&gt;

&lt;p&gt;For an API key, revoke the old key and generate a new one.&lt;/p&gt;

&lt;p&gt;For a password, change it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Credential rotation is more important than simply deleting the leaked secret from the conversation or code.&lt;/strong&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  A Practical Secure Setup
&lt;/h1&gt;

&lt;p&gt;For a small VPS or SaaS project, you don't need a complicated enterprise security architecture.&lt;/p&gt;

&lt;p&gt;A practical setup could look 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;                  +----------------+
                  |  Cursor / AI   |
                  |     Agent      |
                  +-------+--------+
                          |
                          v
                  +----------------+
                  |  Source Code   |
                  +-------+--------+
                          |
                       Git Push
                          |
                          v
                  +----------------+
                  |    GitHub      |
                  +-------+--------+
                          |
                          v
                  +----------------+
                  | CI/CD + Secrets|
                  +-------+--------+
                          |
                     SSH / SFTP
                          |
                          v
                  +----------------+
                  | Deploy User    |
                  +-------+--------+
                          |
                          v
                  +----------------+
                  | Application    |
                  | Directory      |
                  +----------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The AI agent can help you write, test and prepare the application.&lt;/p&gt;

&lt;p&gt;The deployment system handles the production credentials.&lt;/p&gt;

&lt;p&gt;The deployment account has only the permissions required to deploy the application.&lt;/p&gt;




&lt;h1&gt;
  
  
  Security Checklist
&lt;/h1&gt;

&lt;p&gt;Before giving an AI coding agent access to your infrastructure, check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] I am not pasting my master password into an AI chat.&lt;/li&gt;
&lt;li&gt;[ ] Production secrets are not committed to Git.&lt;/li&gt;
&lt;li&gt;[ ] &lt;code&gt;.env&lt;/code&gt; is included in &lt;code&gt;.gitignore&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;[ ] I understand that &lt;code&gt;.env&lt;/code&gt; can still be read by an agent with filesystem access.&lt;/li&gt;
&lt;li&gt;[ ] I use SSH keys where practical.&lt;/li&gt;
&lt;li&gt;[ ] Production uses a dedicated deployment user.&lt;/li&gt;
&lt;li&gt;[ ] The deployment user does not have unnecessary root access.&lt;/li&gt;
&lt;li&gt;[ ] cPanel credentials are scoped to the application.&lt;/li&gt;
&lt;li&gt;[ ] Development, staging and production credentials are separate.&lt;/li&gt;
&lt;li&gt;[ ] Production secrets are stored in CI/CD or a secret manager where appropriate.&lt;/li&gt;
&lt;li&gt;[ ] Database users have only the permissions they need.&lt;/li&gt;
&lt;li&gt;[ ] Secrets do not appear in terminal or CI/CD logs.&lt;/li&gt;
&lt;li&gt;[ ] I can quickly revoke and rotate credentials.&lt;/li&gt;
&lt;li&gt;[ ] Destructive production operations require appropriate human approval.&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Final Thought
&lt;/h1&gt;

&lt;p&gt;AI coding agents are becoming increasingly capable of interacting with real infrastructure.&lt;/p&gt;

&lt;p&gt;The goal should not be to prevent them from doing useful work.&lt;/p&gt;

&lt;p&gt;The goal is to &lt;strong&gt;limit the damage they can cause if something goes wrong&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Don't ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"How can I give Cursor my VPS password?"&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"How can I allow Cursor to perform this task without giving it unnecessary access to my infrastructure?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Use environment variables to keep secrets out of source code, SSH keys instead of passwords where practical, dedicated deployment users instead of root, scoped cPanel accounts instead of master credentials, and CI/CD or secret managers for production secrets.&lt;/p&gt;

&lt;p&gt;The safest credential is not simply the one that is hidden.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It is the one that has the least power necessary to get the job done.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>learning</category>
      <category>beginners</category>
      <category>security</category>
      <category>agents</category>
    </item>
    <item>
      <title>A Beginner's Guide to Learning RPA: Where to Start and What to Learn</title>
      <dc:creator>Sospeter Mong'are</dc:creator>
      <pubDate>Thu, 24 Sep 2026 11:43:38 +0000</pubDate>
      <link>https://dev.to/msnmongare/a-beginners-guide-to-learning-rpa-where-to-start-and-what-to-learn-5cj4</link>
      <guid>https://dev.to/msnmongare/a-beginners-guide-to-learning-rpa-where-to-start-and-what-to-learn-5cj4</guid>
      <description>&lt;p&gt;Robotic Process Automation, commonly known as RPA, is an area of technology that focuses on using software robots to automate repetitive, rule-based tasks that people normally perform on computers.&lt;/p&gt;

&lt;p&gt;If you have ever copied information from an email into Excel, downloaded files from a website, entered data into another system, or generated and sent the same report every day, you have probably encountered a process that could potentially be automated with RPA.&lt;/p&gt;

&lt;p&gt;The good news is that you do not need to be an advanced programmer to start learning RPA.&lt;/p&gt;

&lt;p&gt;You can begin with visual automation tools and gradually learn programming, APIs, databases, cloud services and AI as you become more advanced.&lt;/p&gt;

&lt;p&gt;This guide explains a practical path for someone starting from zero.&lt;/p&gt;

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

&lt;p&gt;Imagine an employee receives an email containing an Excel file every morning.&lt;/p&gt;

&lt;p&gt;They have to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the email.&lt;/li&gt;
&lt;li&gt;Download the Excel file.&lt;/li&gt;
&lt;li&gt;Read the information.&lt;/li&gt;
&lt;li&gt;Open another application.&lt;/li&gt;
&lt;li&gt;Enter the information.&lt;/li&gt;
&lt;li&gt;Save the changes.&lt;/li&gt;
&lt;li&gt;Generate a report.&lt;/li&gt;
&lt;li&gt;Send the report to their manager.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If these steps happen repeatedly and follow predictable rules, an RPA bot can potentially perform them automatically.&lt;/p&gt;

&lt;p&gt;A simple RPA workflow could look 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;Email received
      ↓
Download attachment
      ↓
Read Excel file
      ↓
Validate information
      ↓
Open business application
      ↓
Enter data
      ↓
Generate report
      ↓
Send email
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The goal of RPA is not simply to replace people.&lt;/p&gt;

&lt;p&gt;It is to automate repetitive work so that people can spend more time on tasks that require judgment, communication, creativity and decision-making.&lt;/p&gt;

&lt;h2&gt;
  
  
  What makes a good RPA process?
&lt;/h2&gt;

&lt;p&gt;Not every process should be automated.&lt;/p&gt;

&lt;p&gt;RPA works particularly well when a process is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Repetitive&lt;/li&gt;
&lt;li&gt;Rule-based&lt;/li&gt;
&lt;li&gt;Predictable&lt;/li&gt;
&lt;li&gt;High-volume&lt;/li&gt;
&lt;li&gt;Time-consuming&lt;/li&gt;
&lt;li&gt;Based on structured data&lt;/li&gt;
&lt;li&gt;Performed using existing computer applications&lt;/li&gt;
&lt;/ul&gt;

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

&lt;blockquote&gt;
&lt;p&gt;Every morning, download a report from a website, process it, update an Excel spreadsheet and email the results.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is a good candidate for RPA.&lt;/p&gt;

&lt;p&gt;A process such as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Decide whether a complex customer complaint deserves compensation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;may require human judgment and may not be suitable for traditional RPA alone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Do I need to know programming?
&lt;/h2&gt;

&lt;p&gt;No.&lt;/p&gt;

&lt;p&gt;Many RPA platforms provide visual interfaces where you build workflows by dragging and configuring activities.&lt;/p&gt;

&lt;p&gt;However, learning programming will give you a significant advantage as you become more advanced.&lt;/p&gt;

&lt;p&gt;Useful programming skills include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Python&lt;/li&gt;
&lt;li&gt;JavaScript&lt;/li&gt;
&lt;li&gt;SQL&lt;/li&gt;
&lt;li&gt;REST APIs&lt;/li&gt;
&lt;li&gt;JSON&lt;/li&gt;
&lt;li&gt;Regular expressions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You do not need to master all of these before starting.&lt;/p&gt;

&lt;p&gt;You can learn them gradually as your automation projects become more complex.&lt;/p&gt;

&lt;h1&gt;
  
  
  The main RPA tools to learn
&lt;/h1&gt;

&lt;p&gt;There are many RPA platforms, but beginners do not need to learn all of them.&lt;/p&gt;

&lt;p&gt;I recommend starting with two major tools:&lt;/p&gt;

&lt;h2&gt;
  
  
  1. &lt;a href="https://www.microsoft.com/en-us/power-platform/products/power-automate" rel="noopener noreferrer"&gt;Microsoft Power Automate&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Power Automate is part of Microsoft's automation ecosystem.&lt;/p&gt;

&lt;p&gt;It allows you to automate processes across services such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Outlook&lt;/li&gt;
&lt;li&gt;Excel&lt;/li&gt;
&lt;li&gt;SharePoint&lt;/li&gt;
&lt;li&gt;Teams&lt;/li&gt;
&lt;li&gt;OneDrive&lt;/li&gt;
&lt;li&gt;SQL databases&lt;/li&gt;
&lt;li&gt;HTTP APIs&lt;/li&gt;
&lt;li&gt;Microsoft 365&lt;/li&gt;
&lt;li&gt;Other third-party services&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It also includes &lt;strong&gt;Power Automate Desktop&lt;/strong&gt;, which allows you to automate tasks performed directly on a Windows computer.&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;Open browser
    ↓
Go to website
    ↓
Login
    ↓
Download report
    ↓
Open Excel
    ↓
Process data
    ↓
Save file
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Power Automate is a good starting point, especially if you work in organizations that already use Microsoft 365 and Azure.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. &lt;a href="https://www.uipath.com/" rel="noopener noreferrer"&gt;UiPath&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;UiPath is another major RPA platform.&lt;/p&gt;

&lt;p&gt;It provides tools for building, deploying and managing software robots.&lt;/p&gt;

&lt;p&gt;Important UiPath concepts include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;UiPath Studio&lt;/li&gt;
&lt;li&gt;Activities&lt;/li&gt;
&lt;li&gt;Variables&lt;/li&gt;
&lt;li&gt;Arguments&lt;/li&gt;
&lt;li&gt;Selectors&lt;/li&gt;
&lt;li&gt;DataTables&lt;/li&gt;
&lt;li&gt;Workflows&lt;/li&gt;
&lt;li&gt;Exception handling&lt;/li&gt;
&lt;li&gt;Logging&lt;/li&gt;
&lt;li&gt;Queues&lt;/li&gt;
&lt;li&gt;Orchestrator&lt;/li&gt;
&lt;li&gt;Robots&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;UiPath is worth learning if you want to pursue RPA as a dedicated career path.&lt;/p&gt;

&lt;h1&gt;
  
  
  What should you learn first?
&lt;/h1&gt;

&lt;p&gt;Do not try to learn everything at once.&lt;/p&gt;

&lt;p&gt;A good learning path 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;RPA Fundamentals
       ↓
Power Automate Desktop
       ↓
UiPath
       ↓
Web Automation
       ↓
Excel &amp;amp; Data Processing
       ↓
APIs
       ↓
SQL
       ↓
Python
       ↓
Enterprise RPA
       ↓
AI + RPA
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's break this down.&lt;/p&gt;

&lt;h1&gt;
  
  
  Step 1: Learn RPA fundamentals
&lt;/h1&gt;

&lt;p&gt;Before learning a specific tool, understand the basic concepts.&lt;/p&gt;

&lt;p&gt;Learn:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What RPA is&lt;/li&gt;
&lt;li&gt;What RPA is not&lt;/li&gt;
&lt;li&gt;RPA use cases&lt;/li&gt;
&lt;li&gt;Attended automation&lt;/li&gt;
&lt;li&gt;Unattended automation&lt;/li&gt;
&lt;li&gt;Rule-based processes&lt;/li&gt;
&lt;li&gt;Process discovery&lt;/li&gt;
&lt;li&gt;Workflow design&lt;/li&gt;
&lt;li&gt;Exceptions&lt;/li&gt;
&lt;li&gt;Logging&lt;/li&gt;
&lt;li&gt;Monitoring&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One important concept is &lt;strong&gt;process discovery&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Before building a bot, you should understand the process you are trying to automate.&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;What starts the process?
        ↓
What information is required?
        ↓
What applications are used?
        ↓
What decisions are made?
        ↓
What can go wrong?
        ↓
What should happen when something fails?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is an important skill for an RPA developer.&lt;/p&gt;

&lt;h1&gt;
  
  
  Step 2: Learn Power Automate Desktop
&lt;/h1&gt;

&lt;p&gt;Once you understand the basics, build simple desktop automations.&lt;/p&gt;

&lt;p&gt;Start with:&lt;/p&gt;

&lt;h3&gt;
  
  
  Automation 1 - File management
&lt;/h3&gt;

&lt;p&gt;Build a bot that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Find files in a folder
       ↓
Read filenames
       ↓
Create folders
       ↓
Move files
       ↓
Generate a summary
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Automation 2 - Excel
&lt;/h3&gt;

&lt;p&gt;Build an automation that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Open Excel
     ↓
Read data
     ↓
Filter records
     ↓
Calculate values
     ↓
Create a report
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Automation 3 - Email
&lt;/h3&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Check email
    ↓
Find messages matching a condition
    ↓
Download attachments
    ↓
Process attachment
    ↓
Send response
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Automation 4 - Browser
&lt;/h3&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Open browser
     ↓
Navigate to website
     ↓
Login
     ↓
Read information
     ↓
Save information
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These simple projects will teach you many of the concepts used in real RPA work.&lt;/p&gt;

&lt;h1&gt;
  
  
  Step 3: Learn UiPath
&lt;/h1&gt;

&lt;p&gt;Once you are comfortable with basic automation, start learning UiPath.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Activities
&lt;/h3&gt;

&lt;p&gt;Activities are the building blocks of UiPath workflows.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Open browser&lt;/li&gt;
&lt;li&gt;Click&lt;/li&gt;
&lt;li&gt;Type&lt;/li&gt;
&lt;li&gt;Read text&lt;/li&gt;
&lt;li&gt;Read Excel&lt;/li&gt;
&lt;li&gt;Write Excel&lt;/li&gt;
&lt;li&gt;Send email&lt;/li&gt;
&lt;li&gt;Create file&lt;/li&gt;
&lt;li&gt;Move file&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Variables
&lt;/h3&gt;

&lt;p&gt;Learn how to store information inside your automation.&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;customerName
invoiceNumber
amount
emailAddress
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Conditions
&lt;/h3&gt;

&lt;p&gt;Learn how to make the bot behave differently depending on the data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;IF amount &amp;gt; 100000
    send for approval
ELSE
    process automatically
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Loops
&lt;/h3&gt;

&lt;p&gt;Learn how to process multiple records.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;For each customer
    Validate customer
    Update system
    Generate result
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Selectors
&lt;/h3&gt;

&lt;p&gt;Selectors are particularly important in UI automation.&lt;/p&gt;

&lt;p&gt;They allow an RPA tool to identify elements on a webpage or application.&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;Username field
Password field
Login button
Submit button
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Understanding selectors will help you build more reliable automations.&lt;/p&gt;

&lt;h1&gt;
  
  
  Step 4: Learn Excel and data processing
&lt;/h1&gt;

&lt;p&gt;A lot of business automation involves data.&lt;/p&gt;

&lt;p&gt;You should become comfortable with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Excel&lt;/li&gt;
&lt;li&gt;CSV&lt;/li&gt;
&lt;li&gt;JSON&lt;/li&gt;
&lt;li&gt;XML&lt;/li&gt;
&lt;li&gt;DataTables&lt;/li&gt;
&lt;li&gt;Basic data validation&lt;/li&gt;
&lt;li&gt;Filtering&lt;/li&gt;
&lt;li&gt;Sorting&lt;/li&gt;
&lt;li&gt;Transformations&lt;/li&gt;
&lt;/ul&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;Excel
   ↓
Validate records
   ↓
Remove duplicates
   ↓
Transform data
   ↓
Send to API
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should also learn basic SQL.&lt;/p&gt;

&lt;p&gt;At minimum, understand:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Step 5: Learn APIs
&lt;/h1&gt;

&lt;p&gt;This is one of the most valuable skills you can add to RPA.&lt;/p&gt;

&lt;p&gt;Modern automation does not always need to interact with a website through clicks.&lt;/p&gt;

&lt;p&gt;If a system provides an API, you can often integrate with it directly.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RPA Bot
   ↓
Open browser
   ↓
Click login
   ↓
Click menu
   ↓
Click customer
   ↓
Enter information
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;you may be able to do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RPA
 ↓
REST API
 ↓
Business System
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Learn:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;REST APIs&lt;/li&gt;
&lt;li&gt;HTTP methods&lt;/li&gt;
&lt;li&gt;GET&lt;/li&gt;
&lt;li&gt;POST&lt;/li&gt;
&lt;li&gt;PUT/PATCH&lt;/li&gt;
&lt;li&gt;DELETE&lt;/li&gt;
&lt;li&gt;Headers&lt;/li&gt;
&lt;li&gt;Authentication&lt;/li&gt;
&lt;li&gt;OAuth 2.0&lt;/li&gt;
&lt;li&gt;API keys&lt;/li&gt;
&lt;li&gt;JSON&lt;/li&gt;
&lt;li&gt;Webhooks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is where RPA starts connecting with traditional software engineering.&lt;/p&gt;

&lt;h1&gt;
  
  
  Step 6: Learn Python
&lt;/h1&gt;

&lt;p&gt;Python is not mandatory for RPA, but it can make you much more capable.&lt;/p&gt;

&lt;p&gt;You can use Python for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Data processing&lt;/li&gt;
&lt;li&gt;File manipulation&lt;/li&gt;
&lt;li&gt;API integration&lt;/li&gt;
&lt;li&gt;Complex business logic&lt;/li&gt;
&lt;li&gt;Data transformation&lt;/li&gt;
&lt;li&gt;Automation scripts&lt;/li&gt;
&lt;li&gt;AI integration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Useful libraries include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;pandas&lt;/li&gt;
&lt;li&gt;requests&lt;/li&gt;
&lt;li&gt;openpyxl&lt;/li&gt;
&lt;li&gt;Playwright&lt;/li&gt;
&lt;li&gt;Selenium&lt;/li&gt;
&lt;li&gt;PyAutoGUI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For web automation, Playwright is particularly useful to learn.&lt;/p&gt;

&lt;p&gt;You can eventually build workflows such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RPA
 ↓
Python
 ↓
Process data
 ↓
REST API
 ↓
Database
 ↓
RPA
 ↓
Email notification
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Step 7: Learn enterprise RPA
&lt;/h1&gt;

&lt;p&gt;After learning the basics, start thinking about how automation works in real organizations.&lt;/p&gt;

&lt;p&gt;This introduces concepts such as:&lt;/p&gt;

&lt;h3&gt;
  
  
  Attended automation
&lt;/h3&gt;

&lt;p&gt;A user starts or interacts with the automation.&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;Employee
   ↓
Starts bot
   ↓
Bot processes task
   ↓
Employee continues
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Unattended automation
&lt;/h3&gt;

&lt;p&gt;The automation runs without someone sitting in front of the computer.&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;2:00 AM
   ↓
Robot starts
   ↓
Processes 5,000 records
   ↓
Generates report
   ↓
Sends notification
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should also learn:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Queues&lt;/li&gt;
&lt;li&gt;Scheduling&lt;/li&gt;
&lt;li&gt;Credentials&lt;/li&gt;
&lt;li&gt;Secrets&lt;/li&gt;
&lt;li&gt;Logging&lt;/li&gt;
&lt;li&gt;Monitoring&lt;/li&gt;
&lt;li&gt;Retry mechanisms&lt;/li&gt;
&lt;li&gt;Exception handling&lt;/li&gt;
&lt;li&gt;Audit trails&lt;/li&gt;
&lt;li&gt;Deployment&lt;/li&gt;
&lt;li&gt;Version control&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These concepts become important when automations are running in production.&lt;/p&gt;

&lt;h1&gt;
  
  
  Step 8: Learn AI + RPA
&lt;/h1&gt;

&lt;p&gt;This is an exciting area because AI can extend what traditional RPA can do.&lt;/p&gt;

&lt;p&gt;Traditional RPA is generally good at structured, predictable tasks.&lt;/p&gt;

&lt;p&gt;AI can help with less structured information.&lt;/p&gt;

&lt;p&gt;For example, consider invoices.&lt;/p&gt;

&lt;p&gt;A traditional RPA workflow might expect:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Invoice number = cell A1
Amount = cell B1
Date = cell C1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But invoices can have different layouts.&lt;/p&gt;

&lt;p&gt;AI can help extract information from documents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Invoice
   ↓
OCR / Document AI
   ↓
Extract information
   ↓
Validate
   ↓
RPA
   ↓
Update accounting system
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can eventually learn technologies such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OCR&lt;/li&gt;
&lt;li&gt;Intelligent Document Processing&lt;/li&gt;
&lt;li&gt;Azure AI Document Intelligence&lt;/li&gt;
&lt;li&gt;Large Language Models&lt;/li&gt;
&lt;li&gt;Structured AI output&lt;/li&gt;
&lt;li&gt;AI agents&lt;/li&gt;
&lt;li&gt;Human-in-the-loop workflows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This combination is often referred to as intelligent automation.&lt;/p&gt;

&lt;h1&gt;
  
  
  Build projects instead of only watching tutorials
&lt;/h1&gt;

&lt;p&gt;One of the biggest mistakes beginners make is spending months watching tutorials without building anything.&lt;/p&gt;

&lt;p&gt;After learning a concept, build something.&lt;/p&gt;

&lt;p&gt;Here are some beginner projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project 1 - Employee onboarding
&lt;/h2&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;New employee information
        ↓
Validate information
        ↓
Create folder
        ↓
Generate documents
        ↓
Send welcome email
        ↓
Notify HR
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Project 2 - Invoice processing
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Invoice arrives by email
        ↓
Download attachment
        ↓
Extract information
        ↓
Validate invoice
        ↓
Store information
        ↓
Send notification
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Project 3 - Daily reporting
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Scheduled automation
        ↓
Retrieve data
        ↓
Process data
        ↓
Generate Excel report
        ↓
Send email
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Project 4 - Customer data synchronization
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;System A
    ↓
API
    ↓
Transform data
    ↓
System B
    ↓
Log result
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Project 5 - AI document processing
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Document
   ↓
AI extraction
   ↓
Validate information
   ↓
Human approval if necessary
   ↓
RPA/API
   ↓
Business system
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  RPA skills checklist
&lt;/h1&gt;

&lt;p&gt;As you progress, you can use this checklist.&lt;/p&gt;

&lt;h3&gt;
  
  
  Beginner
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Understand RPA&lt;/li&gt;
&lt;li&gt;[ ] Identify automation opportunities&lt;/li&gt;
&lt;li&gt;[ ] Understand workflows&lt;/li&gt;
&lt;li&gt;[ ] Power Automate Desktop&lt;/li&gt;
&lt;li&gt;[ ] Basic UiPath&lt;/li&gt;
&lt;li&gt;[ ] Excel automation&lt;/li&gt;
&lt;li&gt;[ ] File automation&lt;/li&gt;
&lt;li&gt;[ ] Email automation&lt;/li&gt;
&lt;li&gt;[ ] Browser automation&lt;/li&gt;
&lt;li&gt;[ ] Conditions&lt;/li&gt;
&lt;li&gt;[ ] Loops&lt;/li&gt;
&lt;li&gt;[ ] Variables&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Intermediate
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] UiPath selectors&lt;/li&gt;
&lt;li&gt;[ ] Exception handling&lt;/li&gt;
&lt;li&gt;[ ] Logging&lt;/li&gt;
&lt;li&gt;[ ] Debugging&lt;/li&gt;
&lt;li&gt;[ ] DataTables&lt;/li&gt;
&lt;li&gt;[ ] SQL&lt;/li&gt;
&lt;li&gt;[ ] REST APIs&lt;/li&gt;
&lt;li&gt;[ ] JSON&lt;/li&gt;
&lt;li&gt;[ ] OAuth&lt;/li&gt;
&lt;li&gt;[ ] Python&lt;/li&gt;
&lt;li&gt;[ ] Playwright&lt;/li&gt;
&lt;li&gt;[ ] Power Automate Cloud&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Advanced
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] UiPath Orchestrator&lt;/li&gt;
&lt;li&gt;[ ] Queues&lt;/li&gt;
&lt;li&gt;[ ] Unattended automation&lt;/li&gt;
&lt;li&gt;[ ] Credentials and secrets&lt;/li&gt;
&lt;li&gt;[ ] Monitoring&lt;/li&gt;
&lt;li&gt;[ ] CI/CD&lt;/li&gt;
&lt;li&gt;[ ] Version control&lt;/li&gt;
&lt;li&gt;[ ] Enterprise architecture&lt;/li&gt;
&lt;li&gt;[ ] Intelligent Document Processing&lt;/li&gt;
&lt;li&gt;[ ] AI integration&lt;/li&gt;
&lt;li&gt;[ ] AI agents&lt;/li&gt;
&lt;li&gt;[ ] Human-in-the-loop automation&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Do you need to learn everything?
&lt;/h1&gt;

&lt;p&gt;No.&lt;/p&gt;

&lt;p&gt;You don't need to become an expert in UiPath, Power Automate, Python, APIs, SQL and AI before building your first bot.&lt;/p&gt;

&lt;p&gt;Start small.&lt;/p&gt;

&lt;p&gt;A practical progression is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Learn RPA concepts
       ↓
Build a simple desktop automation
       ↓
Automate Excel
       ↓
Automate email
       ↓
Automate a website
       ↓
Learn APIs
       ↓
Learn SQL
       ↓
Learn Python
       ↓
Build enterprise workflows
       ↓
Add AI
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The most valuable skill is not knowing how to click around an RPA platform.&lt;/p&gt;

&lt;p&gt;It is learning how to look at a business process and answer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What part of this process can be automated, how should it be automated, and how can I make the automation reliable?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once you develop that ability, learning another RPA platform becomes much easier.&lt;/p&gt;

&lt;h1&gt;
  
  
  Final thoughts
&lt;/h1&gt;

&lt;p&gt;RPA sits at an interesting intersection between &lt;strong&gt;software engineering, business processes and automation&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You can start without being a programmer, but programming, APIs, databases and cloud technologies can significantly expand what you are able to build.&lt;/p&gt;

&lt;p&gt;A strong long-term skill set could look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                Automation Engineer
                        |
        +---------------+---------------+
        |               |               |
       RPA             APIs            AI
        |               |               |
   UiPath / PA      REST / OAuth      LLMs
        |               |               |
   Desktop/Web       Python/SQL      Agents
        |               |               |
        +---------------+---------------+
                        |
                 Business Automation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you are starting today, don't try to learn ten tools simultaneously.&lt;/p&gt;

&lt;p&gt;Start with &lt;strong&gt;Power Automate Desktop or UiPath&lt;/strong&gt;, build a few real automations, then add &lt;strong&gt;APIs, SQL and Python&lt;/strong&gt;. Once you understand those foundations, explore &lt;strong&gt;AI-powered automation&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That progression can take you from building simple bots to designing complete business automation solutions.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>powerautomate</category>
      <category>rpa</category>
      <category>programming</category>
    </item>
    <item>
      <title>Understanding Retrieval-Augmented Generation (RAG)</title>
      <dc:creator>Sospeter Mong'are</dc:creator>
      <pubDate>Fri, 18 Sep 2026 15:40:46 +0000</pubDate>
      <link>https://dev.to/msnmongare/understanding-retrieval-augmented-generation-rag-331e</link>
      <guid>https://dev.to/msnmongare/understanding-retrieval-augmented-generation-rag-331e</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Large Language Models (LLMs) have changed how we interact with software. They can answer questions, summarize documents, generate code, analyze information, and perform many other tasks using natural language.&lt;/p&gt;

&lt;p&gt;However, LLMs have an important limitation: they do not automatically know everything about your organization, your internal documents, or information that has changed since their training.&lt;/p&gt;

&lt;p&gt;This is where &lt;strong&gt;Retrieval-Augmented Generation (RAG)&lt;/strong&gt; comes in.&lt;/p&gt;

&lt;p&gt;RAG is an AI architecture that allows an LLM to retrieve relevant information from an external knowledge source before generating a response. Instead of relying entirely on what the model learned during training, the system first searches a collection of trusted information, retrieves relevant content, and provides that content to the LLM as context.&lt;/p&gt;

&lt;p&gt;A simple way to think about RAG is an &lt;strong&gt;open-book exam&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Instead of asking the AI to answer a question entirely from memory, you give it access to the relevant documents first. The AI searches those documents, finds the information it needs, and then uses that information to formulate an answer.&lt;/p&gt;

&lt;p&gt;For example, imagine an employee asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What is our company's Q3 remote work policy?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A general-purpose LLM may not know the company's internal policy. With RAG, the system can search the organization's policy documents, retrieve the relevant section, provide it to the LLM, and generate an answer based on that information.&lt;/p&gt;

&lt;p&gt;This makes RAG particularly useful for organizations working with proprietary, domain-specific, or frequently changing information.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Two Major Pipelines of RAG
&lt;/h1&gt;

&lt;p&gt;A complete RAG system can be thought of as having two major pipelines:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Data Preparation Pipeline&lt;/strong&gt;, also called ingestion&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Runtime Pipeline&lt;/strong&gt;, which happens when a user asks a question&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Understanding this distinction is important because RAG does not simply mean "put documents into an AI model."&lt;/p&gt;

&lt;p&gt;There is significant work that happens before the user ever asks a question.&lt;/p&gt;




&lt;h1&gt;
  
  
  1. The Data Preparation Pipeline
&lt;/h1&gt;

&lt;p&gt;The first step is preparing your data so that an AI system can efficiently search and retrieve relevant information.&lt;/p&gt;

&lt;p&gt;Your source data could come from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PDF documents&lt;/li&gt;
&lt;li&gt;Word documents&lt;/li&gt;
&lt;li&gt;Internal company wikis&lt;/li&gt;
&lt;li&gt;Websites&lt;/li&gt;
&lt;li&gt;Databases&lt;/li&gt;
&lt;li&gt;SharePoint&lt;/li&gt;
&lt;li&gt;Emails&lt;/li&gt;
&lt;li&gt;Knowledge bases&lt;/li&gt;
&lt;li&gt;API responses&lt;/li&gt;
&lt;li&gt;Internal documentation&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;&lt;strong&gt;Documents -&amp;gt; Chunking -&amp;gt; Embeddings -&amp;gt; Vector Database&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Chunking
&lt;/h2&gt;

&lt;p&gt;Large documents are usually too big to retrieve as a single piece.&lt;/p&gt;

&lt;p&gt;Imagine having a 200-page employee handbook. If someone asks about the company's leave policy, you don't want to retrieve the entire 200-page document.&lt;/p&gt;

&lt;p&gt;Instead, the document is divided into smaller pieces called &lt;strong&gt;chunks&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A chunk might contain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A paragraph&lt;/li&gt;
&lt;li&gt;Several paragraphs&lt;/li&gt;
&lt;li&gt;A section&lt;/li&gt;
&lt;li&gt;A group of related sentences&lt;/li&gt;
&lt;/ul&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;Employee Handbook
        |
        v
+-------------------+
| Chunk 1           |
| Introduction      |
+-------------------+
| Chunk 2           |
| Leave Policy      |
+-------------------+
| Chunk 3           |
| Remote Work       |
+-------------------+
| Chunk 4           |
| Benefits          |
+-------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The goal is to create chunks that contain enough information to be useful while remaining small enough for efficient retrieval.&lt;/p&gt;

&lt;p&gt;Chunking is more important than it might initially appear. Poorly chosen chunks can result in the system retrieving incomplete or irrelevant information, which can ultimately affect the quality of the generated answer.&lt;/p&gt;




&lt;h2&gt;
  
  
  Embedding
&lt;/h2&gt;

&lt;p&gt;Once the documents have been divided into chunks, each chunk is passed through an &lt;strong&gt;embedding model&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;An embedding model converts text into a numerical representation called a &lt;strong&gt;vector embedding&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For example, a sentence such as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Employees can work remotely three days per week."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;is converted into a mathematical representation containing many numbers.&lt;/p&gt;

&lt;p&gt;The important idea is that the vector represents the semantic meaning of the text.&lt;/p&gt;

&lt;p&gt;This allows a system to identify relationships between concepts rather than simply matching exact words.&lt;/p&gt;

&lt;p&gt;For example, a user might ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"How many days can I work from home?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The document might contain:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Employees can work remotely three days per week."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Even though the wording is different, the system can recognize that the two pieces of text are semantically related.&lt;/p&gt;




&lt;h2&gt;
  
  
  Storing the Embeddings
&lt;/h2&gt;

&lt;p&gt;The generated embeddings are stored in a &lt;strong&gt;vector database&lt;/strong&gt;.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;ChromaDB&lt;/li&gt;
&lt;li&gt;Pinecone&lt;/li&gt;
&lt;li&gt;Weaviate&lt;/li&gt;
&lt;li&gt;pgvector&lt;/li&gt;
&lt;li&gt;Qdrant&lt;/li&gt;
&lt;li&gt;Milvus&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The vector database acts as a searchable index for the knowledge base.&lt;/p&gt;

&lt;p&gt;A simplified representation might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Document Chunk
      |
      v
Embedding Model
      |
      v
Vector
[0.021, -0.183, 0.492, ...]
      |
      v
Vector Database
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The database can then perform similarity searches to identify which pieces of information are most relevant to a user's question.&lt;/p&gt;




&lt;h1&gt;
  
  
  2. The Runtime Pipeline
&lt;/h1&gt;

&lt;p&gt;The second part of RAG happens when a user actually interacts with the application.&lt;/p&gt;

&lt;p&gt;This is the part most people associate with RAG.&lt;/p&gt;

&lt;p&gt;The basic process is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User Query
     |
     v
Retrieval
     |
     v
Retrieved Context
     |
     v
Augmentation
     |
     v
LLM
     |
     v
Generated Answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are three core stages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Retrieval&lt;/li&gt;
&lt;li&gt;Augmentation&lt;/li&gt;
&lt;li&gt;Generation&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Step 1: Retrieval
&lt;/h2&gt;

&lt;p&gt;The user submits a question.&lt;/p&gt;

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

&lt;blockquote&gt;
&lt;p&gt;"What is our company's Q3 remote work policy?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The system converts the question into an embedding using the same or a compatible embedding model used during ingestion.&lt;/p&gt;

&lt;p&gt;It then searches the vector database for chunks that are semantically similar to the query.&lt;/p&gt;

&lt;p&gt;The vector database might return something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Chunk 1:
"Employees are required to work from the office..."

Chunk 2:
"Employees may work remotely up to three days per week..."

Chunk 3:
"Remote employees must remain available during..."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The system selects the most relevant chunks to provide as context to the LLM.&lt;/p&gt;

&lt;p&gt;This is the &lt;strong&gt;retrieval&lt;/strong&gt; part of Retrieval-Augmented Generation.&lt;/p&gt;




&lt;h1&gt;
  
  
  Step 2: Augmentation
&lt;/h1&gt;

&lt;p&gt;The retrieved information is then combined with the user's original question.&lt;/p&gt;

&lt;p&gt;The application typically uses a prompt template to tell the LLM how to use the retrieved information.&lt;/p&gt;

&lt;p&gt;Conceptually, it could look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Use the following information to answer the user's question.

Context:
Employees may work remotely up to three days per week.
Remote employees must remain available during
normal working hours.

Question:
What is our company's Q3 remote work policy?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The LLM now has access to information that was retrieved specifically for the user's question.&lt;/p&gt;

&lt;p&gt;This is the &lt;strong&gt;augmentation&lt;/strong&gt; part of RAG.&lt;/p&gt;




&lt;h1&gt;
  
  
  Step 3: Generation
&lt;/h1&gt;

&lt;p&gt;The augmented prompt is sent to the LLM.&lt;/p&gt;

&lt;p&gt;The model uses the retrieved context, together with its language understanding and reasoning capabilities, to generate the final response.&lt;/p&gt;

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

&lt;blockquote&gt;
&lt;p&gt;"According to the company's policy, employees may work remotely up to three days per week while remaining available during normal working hours."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is the &lt;strong&gt;generation&lt;/strong&gt; part of RAG.&lt;/p&gt;

&lt;p&gt;Putting everything together:&lt;br&gt;
&lt;/p&gt;

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

Documents
    |
    v
Chunking
    |
    v
Embedding Model
    |
    v
Vector Database
    |
    |
    |                    RUNTIME
    |                    -------
    |                       |
    |                  User Question
    |                       |
    |                       v
    +--------------&amp;gt;  Query Embedding
                            |
                            v
                     Similarity Search
                            |
                            v
                    Relevant Chunks
                            |
                            v
                       Prompt + Context
                            |
                            v
                           LLM
                            |
                            v
                     Generated Answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Why Organizations Use RAG
&lt;/h1&gt;

&lt;p&gt;RAG is particularly useful for organizations that need AI systems to work with their own data.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Grounding AI Responses
&lt;/h2&gt;

&lt;p&gt;LLMs can sometimes generate information that sounds convincing but is incorrect. This is commonly referred to as &lt;strong&gt;hallucination&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;RAG can reduce this problem by providing the model with relevant source information before it generates an answer.&lt;/p&gt;

&lt;p&gt;However, RAG does not magically eliminate hallucinations.&lt;/p&gt;

&lt;p&gt;If the retrieval system returns irrelevant information, incomplete information, or outdated information, the LLM can still produce a poor answer.&lt;/p&gt;

&lt;p&gt;This is why the quality of the retrieval system is just as important as the LLM itself.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Easier Knowledge Updates
&lt;/h2&gt;

&lt;p&gt;Suppose a company changes its leave policy.&lt;/p&gt;

&lt;p&gt;With a traditional approach, you might consider retraining or fine-tuning a model to incorporate the new information.&lt;/p&gt;

&lt;p&gt;With RAG, you can update the underlying knowledge base.&lt;/p&gt;

&lt;p&gt;The new document can be processed, chunked, embedded, and added to the vector database.&lt;/p&gt;

&lt;p&gt;The LLM itself does not need to be retrained simply because the organization's documentation changed.&lt;/p&gt;

&lt;p&gt;This makes RAG particularly useful for information that changes frequently.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Working With Private Data
&lt;/h2&gt;

&lt;p&gt;Organizations have large amounts of internal information that general-purpose AI models were not trained on.&lt;/p&gt;

&lt;p&gt;This could include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Internal policies&lt;/li&gt;
&lt;li&gt;Product documentation&lt;/li&gt;
&lt;li&gt;Customer support information&lt;/li&gt;
&lt;li&gt;Financial reports&lt;/li&gt;
&lt;li&gt;Technical documentation&lt;/li&gt;
&lt;li&gt;Employee handbooks&lt;/li&gt;
&lt;li&gt;Business processes&lt;/li&gt;
&lt;li&gt;Internal knowledge bases&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;RAG provides a way to build an AI interface over this information while keeping the organization's knowledge source separate from the underlying LLM.&lt;/p&gt;

&lt;p&gt;Of course, privacy and security still need to be designed carefully. Access controls, authentication, authorization, data isolation, logging, and appropriate handling of sensitive information remain important.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Cost and Maintenance
&lt;/h2&gt;

&lt;p&gt;Continuously retraining a large language model whenever an organization's information changes is generally impractical.&lt;/p&gt;

&lt;p&gt;RAG separates the &lt;strong&gt;knowledge layer&lt;/strong&gt; from the &lt;strong&gt;language model&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The LLM handles language understanding and generation, while the retrieval system provides the relevant organizational knowledge.&lt;/p&gt;

&lt;p&gt;This separation makes it easier to maintain and update the knowledge source independently.&lt;/p&gt;




&lt;h1&gt;
  
  
  RAG Is More Than a Vector Database
&lt;/h1&gt;

&lt;p&gt;One common misconception is that building a RAG system simply means:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Put documents into a vector database and connect it to an LLM."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In practice, there are several components involved.&lt;/p&gt;

&lt;p&gt;A production RAG system may include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Document ingestion&lt;/li&gt;
&lt;li&gt;Text extraction&lt;/li&gt;
&lt;li&gt;Chunking&lt;/li&gt;
&lt;li&gt;Embedding models&lt;/li&gt;
&lt;li&gt;Vector databases&lt;/li&gt;
&lt;li&gt;Metadata filtering&lt;/li&gt;
&lt;li&gt;Keyword search&lt;/li&gt;
&lt;li&gt;Semantic search&lt;/li&gt;
&lt;li&gt;Hybrid search&lt;/li&gt;
&lt;li&gt;Reranking&lt;/li&gt;
&lt;li&gt;Prompt engineering&lt;/li&gt;
&lt;li&gt;LLMs&lt;/li&gt;
&lt;li&gt;Access control&lt;/li&gt;
&lt;li&gt;Observability&lt;/li&gt;
&lt;li&gt;Evaluation&lt;/li&gt;
&lt;li&gt;Caching&lt;/li&gt;
&lt;li&gt;Data freshness mechanisms&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This means RAG is better understood as an &lt;strong&gt;architecture&lt;/strong&gt; rather than a single technology.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Importance of Retrieval Quality
&lt;/h1&gt;

&lt;p&gt;One of the most important lessons when building RAG systems is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A better LLM cannot always compensate for poor retrieval.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Imagine a user asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What is the company's maternity leave policy?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But the retrieval system returns documents about annual leave.&lt;/p&gt;

&lt;p&gt;The LLM now has the wrong context.&lt;/p&gt;

&lt;p&gt;Even if the LLM is extremely capable, it may generate an answer based on irrelevant information.&lt;/p&gt;

&lt;p&gt;A simplified way to think about the process is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Good Retrieval
      +
Relevant Context
      +
Good LLM
      =
Useful Answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Poor Retrieval
      +
Wrong Context
      +
Good LLM
      =
Potentially Wrong Answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is why RAG evaluation needs to look beyond the final response. You also need to evaluate whether the system retrieved the right information in the first place.&lt;/p&gt;




&lt;h1&gt;
  
  
  RAG and Traditional LLM Applications
&lt;/h1&gt;

&lt;p&gt;A standard LLM application might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User
  |
  v
LLM
  |
  v
Response
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model relies primarily on the information encoded in its parameters during training, plus whatever information is included in the current conversation or prompt.&lt;/p&gt;

&lt;p&gt;A RAG application adds an external knowledge layer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                  +----------------+
                  | Knowledge Base |
                  +-------+--------+
                          |
                          v
User -&amp;gt; Retrieval -&amp;gt; Context -&amp;gt; LLM -&amp;gt; Response
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This architectural difference is what makes RAG useful for organization-specific knowledge.&lt;/p&gt;




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

&lt;p&gt;Consider a healthcare organization with thousands of documents.&lt;/p&gt;

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

&lt;blockquote&gt;
&lt;p&gt;"What is the process for approving a medical claim?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Without RAG, the LLM may have general knowledge about medical claims, but it does not necessarily know the organization's specific process.&lt;/p&gt;

&lt;p&gt;With RAG:&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
     |
     v
Search Knowledge Base
     |
     v
Retrieve Claims Procedure
     |
     v
Add Procedure to Prompt
     |
     v
LLM
     |
     v
Answer Based on Retrieved Procedure
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same architecture can be applied to many other domains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Banking&lt;/li&gt;
&lt;li&gt;Insurance&lt;/li&gt;
&lt;li&gt;Healthcare&lt;/li&gt;
&lt;li&gt;Education&lt;/li&gt;
&lt;li&gt;Legal documentation&lt;/li&gt;
&lt;li&gt;Customer support&lt;/li&gt;
&lt;li&gt;Enterprise IT&lt;/li&gt;
&lt;li&gt;Human resources&lt;/li&gt;
&lt;li&gt;Government services&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Anywhere an organization has a large collection of documents or structured knowledge, RAG can potentially provide a useful interface for accessing that information.&lt;/p&gt;




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

&lt;p&gt;Retrieval-Augmented Generation is one of the most practical architectures for connecting LLMs to external knowledge.&lt;/p&gt;

&lt;p&gt;The fundamental idea is simple:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Retrieve relevant information first, then let the LLM generate an answer using that information.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A RAG system generally consists of two major phases.&lt;/p&gt;

&lt;p&gt;During &lt;strong&gt;data preparation&lt;/strong&gt;, documents are collected, chunked, converted into embeddings, and stored in a searchable knowledge store.&lt;/p&gt;

&lt;p&gt;During &lt;strong&gt;runtime&lt;/strong&gt;, a user's question is converted into a searchable representation, relevant information is retrieved, that information is added to the prompt, and the LLM generates the final response.&lt;/p&gt;

&lt;p&gt;The important thing to remember is that RAG is not just about the LLM. The quality of the entire pipeline matters - from how documents are processed and chunked, to how information is retrieved, filtered, ranked, and finally presented to the model.&lt;/p&gt;

&lt;p&gt;As organizations look for practical ways to use AI with their own data, understanding RAG provides an important foundation for building AI applications that are connected to real, domain-specific knowledge rather than relying solely on what a model learned during training.&lt;/p&gt;

</description>
      <category>rag</category>
      <category>beginners</category>
    </item>
    <item>
      <title>What is Grok Bot? The Complete Beginner's Guide and Use Cases</title>
      <dc:creator>Sospeter Mong'are</dc:creator>
      <pubDate>Fri, 18 Sep 2026 06:57:56 +0000</pubDate>
      <link>https://dev.to/msnmongare/what-is-grok-bot-the-complete-beginners-guide-and-use-cases-58hn</link>
      <guid>https://dev.to/msnmongare/what-is-grok-bot-the-complete-beginners-guide-and-use-cases-58hn</guid>
      <description>&lt;p&gt;Imagine having a personal assistant who doesn't just type out answers, but actually sits down at a computer, opens a web browser, fills out forms, and organizes files for you. That is exactly what Grok Bot does.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;As artificial intelligence evolves, we are moving away from simple chatbots and entering the era of AI "agents"—programs that can actively execute multi-step tasks. In this guide, we will break down what Grok Bot is, how it works, and how you can use it to automate your daily digital chores.&amp;nbsp;&lt;/p&gt;

&lt;h3&gt;
  
  
  What is Grok Bot?
&lt;/h3&gt;

&lt;p&gt;At its core, &lt;strong&gt;Grok Bot&lt;/strong&gt; is an autonomous AI assistant designed to interact with a computer just like a human would. Unlike traditional AI that only generates text or code, Grok Bot is equipped with a dedicated, persistent Linux virtual machine in the cloud.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;This cloud computer gives the bot its own:&amp;nbsp;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Web Browser:&lt;/strong&gt; To navigate websites, click buttons, and log into portals.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;File Manager:&lt;/strong&gt; To download, organize, and edit documents or spreadsheets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Terminal:&lt;/strong&gt; To run commands and execute lightweight scripts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because it operates on an independent cloud machine, it is always on. You can give Grok Bot a complex assignment, close your laptop, walk away, and it will keep working in the background until the job is done.&amp;nbsp;&lt;/p&gt;

&lt;h3&gt;
  
  
  Top Use Cases for Grok Bot
&lt;/h3&gt;

&lt;p&gt;Because Grok Bot can use the internet and manage files, its practical applications are almost limitless. Here are the most common ways people use it:&amp;nbsp;&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Automating Boring Workflows
&lt;/h3&gt;

&lt;p&gt;We all have repetitive digital tasks that eat up our time. Grok Bot can take over these chores entirely. For example, it can log into your company’s vendor portal every Friday, download invoice data, and manually type that information into an accounting spreadsheet. It is perfect for moving data between two software platforms that do not easily talk to each other.&amp;nbsp;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Deep Web Research and Monitoring
&lt;/h3&gt;

&lt;p&gt;Instead of spending hours searching the web, you can deploy Grok Bot to do the heavy lifting. It can scan competitor websites to track pricing changes, gather contact details from public directories to build lead lists, or monitor retail sites for hard-to-find inventory, instantly alerting you when an item comes back in stock.&amp;nbsp;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. File and Data Management
&lt;/h3&gt;

&lt;p&gt;If you deal with large volumes of data, Grok Bot can act as your digital organizer. It can look through a folder containing thousands of downloaded files, rename them based on specific rules, convert them from PDFs to text files, and filter out duplicate entries using its built-in terminal tools.&amp;nbsp;&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Software and QA Testing
&lt;/h3&gt;

&lt;p&gt;For developers and businesses, Grok Bot can act as a real user. You can instruct it to visit your newly built website, click through the checkout process, and report back if any links are broken or if the system crashes.&amp;nbsp;&lt;/p&gt;

&lt;h3&gt;
  
  
  What Else Should Beginners Know?
&lt;/h3&gt;

&lt;p&gt;Before you dive in, there are a few essential things to keep in mind to get the most out of your AI assistant:&amp;nbsp;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A Shared Environment:&lt;/strong&gt; If you build or deploy multiple bots under your account, they all share access to the same single cloud machine, file system, and browser sessions. This makes it easy for different bots to collaborate on the same set of files.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security and Credentials:&lt;/strong&gt; Because Grok Bot needs to log into websites to do its job, you will need to manage security credentials. Always practice safe data habits by using secure credential managers or dedicated account permissions for your bot.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Beginner-Friendly Controls:&lt;/strong&gt; You don't need to be a computer programmer to use Grok Bot. You can guide it using plain, natural language instructions, and it will translate your goals into clicks, typing, and commands.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Grok Bot represents a massive leap forward in personal productivity. By pairing advanced artificial intelligence with a fully functional cloud computer, it transforms from a simple conversation partner into a capable digital worker. Whether you are trying to escape repetitive data entry, track market trends, or manage messy file directories, Grok Bot can handle the heavy lifting—leaving you free to focus on the work that actually matters.&lt;/p&gt;

</description>
      <category>twitter</category>
      <category>productivity</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How to deploy a Laravel app to a VPS</title>
      <dc:creator>Sospeter Mong'are</dc:creator>
      <pubDate>Wed, 16 Sep 2026 18:57:04 +0000</pubDate>
      <link>https://dev.to/msnmongare/how-to-deploy-a-laravel-app-to-a-vps-1m7j</link>
      <guid>https://dev.to/msnmongare/how-to-deploy-a-laravel-app-to-a-vps-1m7j</guid>
      <description>&lt;p&gt;This is a beginner guide for a Laravel app where the pages and the API live in the same project. Blade is the frontend. Laravel routes under &lt;code&gt;/api&lt;/code&gt; are the backend. There is no separate Node server.&lt;/p&gt;

&lt;p&gt;Replace &lt;code&gt;YOUR_SERVER_IP&lt;/code&gt;, &lt;code&gt;YOUR_DOMAIN&lt;/code&gt;, &lt;code&gt;YOUR_APP&lt;/code&gt;, and &lt;code&gt;STRONG_PASSWORD&lt;/code&gt; with your own values. Do not copy a real password, &lt;code&gt;APP_KEY&lt;/code&gt;, or a private key into a chat, a screenshot, or git.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;A VPS is a Linux computer in a data center. Visitors type a domain. An A record points that domain at the server's IP. Nginx receives HTTPS and hands every URL to PHP.&lt;/p&gt;

&lt;p&gt;One Laravel app answers both kinds of request. They share &lt;code&gt;public/index.php&lt;/code&gt;, the same &lt;code&gt;.env&lt;/code&gt;, and the same MySQL database.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Request&lt;/th&gt;
&lt;th&gt;Who answers&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;https://YOUR_DOMAIN/&lt;/code&gt; and &lt;code&gt;/login&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Laravel &lt;code&gt;routes/web.php&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Blade pages&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;https://YOUR_DOMAIN/api/...&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Laravel &lt;code&gt;routes/api.php&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;The API in the same app&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MySQL&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;127.0.0.1:3306&lt;/code&gt; only&lt;/td&gt;
&lt;td&gt;Laravel's own database. It is not on the public internet&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;There is no second port to open. PHP-FPM is a local socket, not a public port. Nginx is the only door.&lt;/p&gt;

&lt;p&gt;GitHub Actions cannot upload code until the server already has a folder, a database, &lt;code&gt;.env&lt;/code&gt;, Nginx, and an SSH key. Actions logs in as &lt;code&gt;deploy&lt;/code&gt;, not as your sudo account. A leaked GitHub key can update the app, but it cannot install packages or change the firewall.&lt;/p&gt;

&lt;p&gt;Two accounts, two jobs:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;User&lt;/th&gt;
&lt;th&gt;Job&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;YOUR_SUDO_USER&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The account you SSH in as. It has &lt;code&gt;sudo&lt;/code&gt;. On the Contabo server used for MtandaoBilling this account is &lt;code&gt;sos&lt;/code&gt;. A lot of VPS images do not give you a root password.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;deploy&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Owns &lt;code&gt;/var/www/YOUR_DOMAIN&lt;/code&gt;, runs Composer and Artisan, and is the GitHub Actions login. It has no password.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;PHP itself runs as &lt;code&gt;www-data&lt;/code&gt;. That user must be able to write &lt;code&gt;storage/&lt;/code&gt; and &lt;code&gt;bootstrap/cache&lt;/code&gt; even though &lt;code&gt;deploy&lt;/code&gt; owns the files.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Log in, and do not SSH as deploy yet
&lt;/h2&gt;

&lt;p&gt;From your PC:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh YOUR_SUDO_USER@YOUR_SERVER_IP
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You are on the server. The prompt looks like &lt;code&gt;YOUR_SUDO_USER@your-server:~$&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Do not run &lt;code&gt;ssh deploy@YOUR_SERVER_IP&lt;/code&gt; from that prompt. &lt;code&gt;deploy&lt;/code&gt; has no password and, at this point, no SSH key. The server answers &lt;code&gt;Permission denied (publickey)&lt;/code&gt;. You are already on the machine. Switch user instead:&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="nt"&gt;-u&lt;/span&gt; deploy &lt;span class="nt"&gt;-i&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;sudo&lt;/code&gt; means "run this as another user." &lt;code&gt;-u deploy -i&lt;/code&gt; opens a login shell as &lt;code&gt;deploy&lt;/code&gt;. Type &lt;code&gt;exit&lt;/code&gt; to go back to your sudo account.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;apt&lt;/code&gt;, Nginx, and MySQL stay on the sudo account. Files inside the app folder are edited as &lt;code&gt;deploy&lt;/code&gt;, after that user owns the folder.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Install what Laravel needs
&lt;/h2&gt;

&lt;p&gt;You do not need PM2. PHP-FPM already stays running. You do need Node once, on the machine that builds CSS and JavaScript. On the server, Composer installs PHP packages. Vite assets are usually built in GitHub Actions and uploaded, so the server does not have to run &lt;code&gt;npm&lt;/code&gt; if that build already happened.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Package&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nginx&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Receives HTTPS and sends PHP files to PHP-FPM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;php8.3-fpm&lt;/code&gt; and PHP extensions&lt;/td&gt;
&lt;td&gt;Runs Blade pages and the &lt;code&gt;/api&lt;/code&gt; routes. &lt;code&gt;fpm&lt;/code&gt; is the PHP process Nginx talks to&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;composer&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Installs Laravel's PHP libraries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mysql-server&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Laravel's database&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;certbot&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Gets a free HTTPS certificate from Let's Encrypt&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;acl&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Lets &lt;code&gt;www-data&lt;/code&gt; write logs and sessions in a folder owned by &lt;code&gt;deploy&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ufw&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Firewall. Only SSH, HTTP, and HTTPS should be open&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;As &lt;code&gt;YOUR_SUDO_USER&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;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="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; nginx mysql-server certbot python3-certbot-nginx &lt;span class="se"&gt;\&lt;/span&gt;
  git curl unzip ufw acl &lt;span class="se"&gt;\&lt;/span&gt;
  php8.3-fpm php8.3-cli php8.3-mysql php8.3-mbstring php8.3-xml &lt;span class="se"&gt;\&lt;/span&gt;
  php8.3-curl php8.3-zip php8.3-bcmath php8.3-tokenizer

curl &lt;span class="nt"&gt;-sS&lt;/span&gt; https://getcomposer.org/installer | &lt;span class="nb"&gt;sudo &lt;/span&gt;php &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--install-dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/local/bin &lt;span class="nt"&gt;--filename&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;composer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;php8.3-*&lt;/code&gt; is not in your Ubuntu version, install PHP 8.2 or newer and later point Nginx at that socket (&lt;code&gt;ls /run/php/&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Check that the commands exist:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php &lt;span class="nt"&gt;-v&lt;/span&gt;
composer &lt;span class="nt"&gt;-V&lt;/span&gt;
nginx &lt;span class="nt"&gt;-v&lt;/span&gt;
mysql &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create &lt;code&gt;deploy&lt;/code&gt; once, if it does not exist. &lt;code&gt;--disabled-password&lt;/code&gt; is deliberate. GitHub will log in with a key, not a password that can be guessed.&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;adduser &lt;span class="nt"&gt;--disabled-password&lt;/span&gt; &lt;span class="nt"&gt;--gecos&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt; deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open only SSH and the web ports. Do not open MySQL port 3306.&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;ufw allow OpenSSH
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw allow &lt;span class="s1"&gt;'Nginx Full'&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw &lt;span class="nb"&gt;enable&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;OpenSSH&lt;/code&gt; keeps your current login alive. &lt;code&gt;Nginx Full&lt;/code&gt; is ports 80 and 443.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Make the app folder, and make deploy the owner
&lt;/h2&gt;

&lt;p&gt;Nginx will not use this folder as the website root. It uses &lt;code&gt;public/&lt;/code&gt; inside it. The rest of Laravel (&lt;code&gt;.env&lt;/code&gt;, &lt;code&gt;app/&lt;/code&gt;, &lt;code&gt;vendor/&lt;/code&gt;) must stay outside the web root so visitors cannot 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 mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /var/www/YOUR_DOMAIN
&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; deploy:deploy /var/www/YOUR_DOMAIN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;chown&lt;/code&gt; means "this user owns these files."&lt;/p&gt;

&lt;p&gt;A folder created earlier as root stays owned by root. &lt;code&gt;mkdir -p&lt;/code&gt; as &lt;code&gt;deploy&lt;/code&gt; does not change a folder that already exists, and &lt;code&gt;nano&lt;/code&gt; then fails with &lt;code&gt;Permission denied&lt;/code&gt;. Check with:&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;ls&lt;/span&gt; &lt;span class="nt"&gt;-ld&lt;/span&gt; /var/www/YOUR_DOMAIN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The owner must be &lt;code&gt;deploy deploy&lt;/code&gt;. If it says &lt;code&gt;root root&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; deploy:deploy /var/www/YOUR_DOMAIN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Create the MySQL database
&lt;/h2&gt;

&lt;p&gt;The Linux user &lt;code&gt;deploy&lt;/code&gt; is not the database user. Create a separate MySQL user. In MySQL, &lt;code&gt;localhost&lt;/code&gt; and &lt;code&gt;127.0.0.1&lt;/code&gt; are two different accounts.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;localhost&lt;/code&gt; means "connect through a Unix socket." &lt;code&gt;127.0.0.1&lt;/code&gt; means "connect with TCP." If &lt;code&gt;.env&lt;/code&gt; says &lt;code&gt;DB_HOST=127.0.0.1&lt;/code&gt;, a user created only &lt;code&gt;@localhost&lt;/code&gt; is invisible. &lt;code&gt;ALTER USER&lt;/code&gt; for the TCP account then fails with &lt;code&gt;ERROR 1396&lt;/code&gt; because that account does not exist yet.&lt;/p&gt;

&lt;p&gt;PHP's MySQL driver can use MySQL 8's normal &lt;code&gt;caching_sha2_password&lt;/code&gt; plugin. You do not need &lt;code&gt;mysql_native_password&lt;/code&gt; unless this same database is also used by Prisma. This guide uses the normal plugin.&lt;/p&gt;

&lt;p&gt;As &lt;code&gt;YOUR_SUDO_USER&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;sudo mysql&lt;/code&gt; works without a MySQL root password because Ubuntu lets the system admin in through the socket. Invent a long password. Call it &lt;code&gt;STRONG_PASSWORD&lt;/code&gt; here. Use the same string later in &lt;code&gt;.env&lt;/code&gt;. Do not reuse a password from a chat log.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;your_app&lt;/span&gt; &lt;span class="nb"&gt;CHARACTER&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;utf8mb4&lt;/span&gt; &lt;span class="k"&gt;COLLATE&lt;/span&gt; &lt;span class="n"&gt;utf8mb4_unicode_ci&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;USER&lt;/span&gt; &lt;span class="s1"&gt;'your_app_user'&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="s1"&gt;'127.0.0.1'&lt;/span&gt; &lt;span class="n"&gt;IDENTIFIED&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="s1"&gt;'STRONG_PASSWORD'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;ALL&lt;/span&gt; &lt;span class="k"&gt;PRIVILEGES&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;your_app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="s1"&gt;'your_app_user'&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="s1"&gt;'127.0.0.1'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;FLUSH&lt;/span&gt; &lt;span class="k"&gt;PRIVILEGES&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;host&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;mysql&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;user&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'your_app_user'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;host&lt;/code&gt; column must include &lt;code&gt;127.0.0.1&lt;/code&gt;. Then type &lt;code&gt;exit&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Write &lt;code&gt;.env&lt;/code&gt; on the server only
&lt;/h2&gt;

&lt;p&gt;There is one &lt;code&gt;.env&lt;/code&gt;, at the Laravel project root, not a separate frontend file. GitHub must not upload it.&lt;/p&gt;

&lt;p&gt;As &lt;code&gt;deploy&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 /var/www/YOUR_DOMAIN/.env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start from your project's &lt;code&gt;.env.example&lt;/code&gt; and set at least these. Leave &lt;code&gt;APP_KEY&lt;/code&gt; empty. Artisan fills it after Composer has run.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;APP_NAME=YOUR_APP
APP_ENV=production
APP_KEY=
APP_DEBUG=false
APP_URL=https://YOUR_DOMAIN

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=your_app
DB_USERNAME=your_app_user
DB_PASSWORD=STRONG_PASSWORD

SESSION_DRIVER=database
CACHE_STORE=database
QUEUE_CONNECTION=database
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;APP_DEBUG=false&lt;/code&gt; so a crash does not print secrets in the browser. The real error still goes to &lt;code&gt;storage/logs/laravel.log&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;SESSION_DRIVER=database&lt;/code&gt; is fine for a Laravel app that owns MySQL, but only after &lt;code&gt;php artisan migrate&lt;/code&gt; has created the &lt;code&gt;sessions&lt;/code&gt; table. If you want the first page to work before that migrate, set &lt;code&gt;SESSION_DRIVER=file&lt;/code&gt; and &lt;code&gt;CACHE_STORE=file&lt;/code&gt; instead. A 500 that says &lt;code&gt;no such table: sessions&lt;/code&gt; means the driver is &lt;code&gt;database&lt;/code&gt; and migrate has not run.&lt;/p&gt;

&lt;p&gt;As the sudo user, lock the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo chown &lt;/span&gt;deploy:deploy /var/www/YOUR_DOMAIN/.env
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;600 /var/www/YOUR_DOMAIN/.env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;600&lt;/code&gt; means owner read/write, everyone else nothing.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Install Nginx and the free certificate
&lt;/h2&gt;

&lt;p&gt;The website root is &lt;code&gt;public/&lt;/code&gt;, not the project root. If you point Nginx at &lt;code&gt;/var/www/YOUR_DOMAIN&lt;/code&gt;, visitors can request &lt;code&gt;.env&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As &lt;code&gt;YOUR_SUDO_USER&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/nginx/sites-available/YOUR_DOMAIN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Paste this. Read the comments. They are the reason each line exists.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="s"&gt;[::]:80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;YOUR_DOMAIN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;# Only public/ is on the internet. app/, .env, and vendor/ are not.&lt;/span&gt;
    &lt;span class="kn"&gt;root&lt;/span&gt; &lt;span class="n"&gt;/var/www/YOUR_DOMAIN/public&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;index&lt;/span&gt; &lt;span class="s"&gt;index.php&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;access_log&lt;/span&gt; &lt;span class="n"&gt;/var/log/nginx/YOUR_APP.access.log&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;error_log&lt;/span&gt;  &lt;span class="n"&gt;/var/log/nginx/YOUR_APP.error.log&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;charset&lt;/span&gt; &lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;client_max_body_size&lt;/span&gt; &lt;span class="mi"&gt;20m&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;# Real files (CSS, images, Vite /build) are sent as files.&lt;/span&gt;
    &lt;span class="c1"&gt;# Every other URL, including /api/..., goes to public/index.php.&lt;/span&gt;
    &lt;span class="c1"&gt;# Laravel then chooses web.php or api.php. Nginx does not split them.&lt;/span&gt;
    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;try_files&lt;/span&gt; &lt;span class="nv"&gt;$uri&lt;/span&gt; &lt;span class="nv"&gt;$uri&lt;/span&gt;&lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="n"&gt;/index.php?&lt;/span&gt;&lt;span class="nv"&gt;$query_string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;/favicon.ico&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kn"&gt;access_log&lt;/span&gt; &lt;span class="no"&gt;off&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="kn"&gt;log_not_found&lt;/span&gt; &lt;span class="no"&gt;off&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;/robots.txt&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kn"&gt;access_log&lt;/span&gt; &lt;span class="no"&gt;off&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="kn"&gt;log_not_found&lt;/span&gt; &lt;span class="no"&gt;off&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kn"&gt;error_page&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt; &lt;span class="n"&gt;/index.php&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;# PHP-FPM socket. If this file does not exist, run: ls /run/php/&lt;/span&gt;
    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="p"&gt;~&lt;/span&gt; &lt;span class="sr"&gt;\.php$&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;include&lt;/span&gt; &lt;span class="nc"&gt;snippets/fastcgi-php&lt;/span&gt;&lt;span class="s"&gt;.conf&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;fastcgi_pass&lt;/span&gt; &lt;span class="s"&gt;unix:/run/php/php8.3-fpm.sock&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;fastcgi_param&lt;/span&gt; &lt;span class="s"&gt;HTTPS&lt;/span&gt; &lt;span class="nv"&gt;$https&lt;/span&gt; &lt;span class="s"&gt;if_not_empty&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;# Block .env and .git if they ever land inside public/.&lt;/span&gt;
    &lt;span class="c1"&gt;# /.well-known/ stays open so Certbot can prove you own the domain.&lt;/span&gt;
    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="p"&gt;~&lt;/span&gt; &lt;span class="sr"&gt;/\.(?!well-known).*&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;deny&lt;/span&gt; &lt;span class="s"&gt;all&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enable the site and test it before you reload. &lt;code&gt;nginx -t&lt;/code&gt; stops a typo from taking every site on the server down.&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 ln&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; /etc/nginx/sites-available/YOUR_DOMAIN /etc/nginx/sites-enabled/
&lt;span class="nb"&gt;sudo &lt;/span&gt;nginx &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl reload nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A 404 or a PHP error now is normal. The app is not uploaded yet.&lt;/p&gt;

&lt;p&gt;Then ask Let's Encrypt for a certificate. Certbot is free. The prompt "agree to register with the ACME server" is their terms, not a paid account. Type yes, and enter an email so they can warn you before a certificate expires.&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;certbot &lt;span class="nt"&gt;--nginx&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; YOUR_DOMAIN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Certbot edits the Nginx file on the server. It adds port 443 and redirects HTTP to HTTPS. Do not copy those certificate paths into git.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Give GitHub a key that can log in as deploy
&lt;/h2&gt;

&lt;p&gt;A password in GitHub is worse than a key you can delete. The key has two halves:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Where it goes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;YOUR_APP_deploy.pub&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The server. This is public. It says "this key is allowed."&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;YOUR_APP_deploy&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Your PC, then the GitHub secret. This is private. Never commit it, and never paste it into a chat.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;On your PC, not inside the SSH session:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh-keygen &lt;span class="nt"&gt;-t&lt;/span&gt; ed25519 &lt;span class="nt"&gt;-C&lt;/span&gt; &lt;span class="s2"&gt;"github-actions-YOUR_APP"&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; ~/.ssh/YOUR_APP_deploy &lt;span class="nt"&gt;-N&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
scp ~/.ssh/YOUR_APP_deploy.pub YOUR_SUDO_USER@YOUR_SERVER_IP:~/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In PowerShell, if &lt;code&gt;~&lt;/code&gt; is not your user folder, use the full path, for example &lt;code&gt;C:\Users\ADMIN\.ssh\YOUR_APP_deploy.pub&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;On the VPS, as &lt;code&gt;YOUR_SUDO_USER&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /home/deploy/.ssh
&lt;span class="nb"&gt;sudo tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; /home/deploy/.ssh/authorized_keys &amp;lt; ~/YOUR_APP_deploy.pub
&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; deploy:deploy /home/deploy/.ssh
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;700 /home/deploy/.ssh
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;600 /home/deploy/.ssh/authorized_keys
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;authorized_keys&lt;/code&gt; is the list of public keys allowed to log in as that user. &lt;code&gt;700&lt;/code&gt; and &lt;code&gt;600&lt;/code&gt; are not optional. SSH ignores a key file if other users can read it.&lt;/p&gt;

&lt;p&gt;Test from your PC. You should land in &lt;code&gt;/home/deploy&lt;/code&gt; with no password:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh &lt;span class="nt"&gt;-i&lt;/span&gt; ~/.ssh/YOUR_APP_deploy deploy@YOUR_SERVER_IP
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  8. Add the GitHub secrets
&lt;/h2&gt;

&lt;p&gt;In the repository: Settings, then Secrets and variables, then Actions, then New repository secret.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Secret&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SSH_HOST&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;YOUR_SERVER_IP&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SSH_USERNAME&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;deploy&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SSH_PRIVATE_KEY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The whole private file, including the &lt;code&gt;BEGIN&lt;/code&gt; and &lt;code&gt;END&lt;/code&gt; lines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SSH_PORT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;22&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DEPLOY_PATH&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/var/www/YOUR_DOMAIN&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Show the private key on your PC only, then paste it into GitHub. Use the file without &lt;code&gt;.pub&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; ~/.ssh/YOUR_APP_deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A small workflow you can put in &lt;code&gt;.github/workflows/deploy.yml&lt;/code&gt; of that Laravel project. It builds the Vite files in GitHub, uploads the app, and does not upload &lt;code&gt;.env&lt;/code&gt; or &lt;code&gt;node_modules&lt;/code&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy Laravel&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;shivammathur/setup-php@v2&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;php-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8.3"&lt;/span&gt;
          &lt;span class="na"&gt;tools&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;composer:v2&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;20"&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PHP dependencies and Vite build&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;composer install --no-interaction --prefer-dist --optimize-autoloader --no-dev&lt;/span&gt;
          &lt;span class="s"&gt;npm ci&lt;/span&gt;
          &lt;span class="s"&gt;npm run build&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Upload&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;appleboy/scp-action@v0.1.7&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&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;${{ secrets.SSH_HOST }}&lt;/span&gt;
          &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SSH_USERNAME }}&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SSH_PRIVATE_KEY }}&lt;/span&gt;
          &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SSH_PORT || 22 }}&lt;/span&gt;
          &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.,!node_modules,!.env"&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;${{ secrets.DEPLOY_PATH }}&lt;/span&gt;
          &lt;span class="na"&gt;overwrite&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Migrate and refresh caches&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;appleboy/ssh-action@v1.2.0&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&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;${{ secrets.SSH_HOST }}&lt;/span&gt;
          &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SSH_USERNAME }}&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SSH_PRIVATE_KEY }}&lt;/span&gt;
          &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SSH_PORT || 22 }}&lt;/span&gt;
          &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;cd "${{ secrets.DEPLOY_PATH }}"&lt;/span&gt;
            &lt;span class="s"&gt;composer install --no-dev --optimize-autoloader --no-interaction&lt;/span&gt;
            &lt;span class="s"&gt;php artisan storage:link || true&lt;/span&gt;
            &lt;span class="s"&gt;php artisan migrate --force&lt;/span&gt;
            &lt;span class="s"&gt;php artisan config:cache&lt;/span&gt;
            &lt;span class="s"&gt;php artisan route:cache&lt;/span&gt;
            &lt;span class="s"&gt;php artisan view:cache&lt;/span&gt;
            &lt;span class="s"&gt;sudo systemctl reload php8.3-fpm&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;deploy&lt;/code&gt; cannot run &lt;code&gt;sudo systemctl reload&lt;/code&gt; unless you allow that one command. If the reload line fails, SSH in as &lt;code&gt;YOUR_SUDO_USER&lt;/code&gt; and run &lt;code&gt;sudo systemctl reload php8.3-fpm&lt;/code&gt; yourself after the first deploy. PHP keeps the old cached config until that reload.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;public/build&lt;/code&gt; must be in the upload. If GitHub skips &lt;code&gt;npm run build&lt;/code&gt;, Laravel pages that use &lt;code&gt;@vite&lt;/code&gt; return 500 with "Vite manifest not found."&lt;/p&gt;

&lt;h2&gt;
  
  
  9. First boot on the server
&lt;/h2&gt;

&lt;p&gt;After the first upload, as &lt;code&gt;deploy&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /var/www/YOUR_DOMAIN
composer &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-dev&lt;/span&gt; &lt;span class="nt"&gt;--optimize-autoloader&lt;/span&gt; &lt;span class="nt"&gt;--no-interaction&lt;/span&gt;
php artisan key:generate &lt;span class="nt"&gt;--force&lt;/span&gt;
php artisan migrate &lt;span class="nt"&gt;--force&lt;/span&gt;
php artisan storage:link
php artisan config:cache
php artisan route:cache
php artisan view:cache
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;key:generate&lt;/code&gt; writes &lt;code&gt;APP_KEY&lt;/code&gt;. Without it, every page is 500. Do not run it again on later deploys. A new key logs everybody out and can make old encrypted cookies unreadable.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;migrate --force&lt;/code&gt; creates tables, including &lt;code&gt;sessions&lt;/code&gt; if you use the database session driver. &lt;code&gt;--force&lt;/code&gt; is required because Laravel refuses to migrate when &lt;code&gt;APP_ENV=production&lt;/code&gt; unless you say so.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;storage:link&lt;/code&gt; makes &lt;code&gt;public/storage&lt;/code&gt; point at uploaded files. Skip the error if the link already exists.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;config:cache&lt;/code&gt; bakes &lt;code&gt;.env&lt;/code&gt; into one PHP file. After this, editing &lt;code&gt;.env&lt;/code&gt; does nothing until you run &lt;code&gt;config:cache&lt;/code&gt; again.&lt;/p&gt;

&lt;p&gt;PHP must be able to write logs and sessions. As &lt;code&gt;YOUR_SUDO_USER&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /var/www/YOUR_DOMAIN/storage/framework/&lt;span class="o"&gt;{&lt;/span&gt;cache/data,sessions,views&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  /var/www/YOUR_DOMAIN/storage/logs &lt;span class="se"&gt;\&lt;/span&gt;
  /var/www/YOUR_DOMAIN/bootstrap/cache
&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; deploy:www-data /var/www/YOUR_DOMAIN/storage /var/www/YOUR_DOMAIN/bootstrap/cache
&lt;span class="nb"&gt;sudo chmod&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; ug+rwX /var/www/YOUR_DOMAIN/storage /var/www/YOUR_DOMAIN/bootstrap/cache
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl reload php8.3-fpm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;www-data&lt;/code&gt; is the PHP user. The group write bit is what lets PHP save sessions and log a 500 instead of dying with &lt;code&gt;Permission denied&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. Confirm the site
&lt;/h2&gt;

&lt;p&gt;Open &lt;code&gt;https://YOUR_DOMAIN/&lt;/code&gt; and one API URL, for example &lt;code&gt;https://YOUR_DOMAIN/api/user&lt;/code&gt; or whatever your &lt;code&gt;routes/api.php&lt;/code&gt; exposes. A 401 on a protected API route is fine. It means Laravel answered. A 404 from Nginx, with no Laravel styling, means the request never reached &lt;code&gt;index.php&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If the page is 500:&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 grep&lt;/span&gt; &lt;span class="s2"&gt;"production.ERROR"&lt;/span&gt; /var/www/YOUR_DOMAIN/storage/logs/laravel.log | &lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first &lt;code&gt;production.ERROR&lt;/code&gt; line is the cause. The long stack under it is not.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What the log says&lt;/th&gt;
&lt;th&gt;What to do&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;No application encryption key&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;php artisan key:generate --force&lt;/code&gt;, then &lt;code&gt;config:cache&lt;/code&gt;, then reload PHP-FPM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;no such table: sessions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;php artisan migrate --force&lt;/code&gt;, or set &lt;code&gt;SESSION_DRIVER=file&lt;/code&gt; and cache config again&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;Permission denied&lt;/code&gt; under &lt;code&gt;storage/&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Repeat the &lt;code&gt;chown&lt;/code&gt; and &lt;code&gt;chmod&lt;/code&gt; in step 9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Vite manifest not found&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;npm run build&lt;/code&gt; in GitHub (or on the server) so &lt;code&gt;public/build/manifest.json&lt;/code&gt; exists&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Access denied for user&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;DB_USERNAME&lt;/code&gt;, &lt;code&gt;DB_PASSWORD&lt;/code&gt;, and &lt;code&gt;DB_HOST=127.0.0.1&lt;/code&gt; must match the MySQL user from step 4&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;After any &lt;code&gt;.env&lt;/code&gt; change:&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; /var/www/YOUR_DOMAIN
php artisan config:cache
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then as the sudo user: &lt;code&gt;sudo systemctl reload php8.3-fpm&lt;/code&gt;.&lt;/p&gt;

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

&lt;p&gt;Prepare the server before GitHub deploys. The order is: PHP, Composer, Nginx, and MySQL, a &lt;code&gt;deploy&lt;/code&gt; user, a folder that user owns, a MySQL user at &lt;code&gt;127.0.0.1&lt;/code&gt;, one &lt;code&gt;.env&lt;/code&gt;, Certbot, an SSH key, then a push.&lt;/p&gt;

&lt;p&gt;Blade pages and &lt;code&gt;/api&lt;/code&gt; are the same app. Nginx sends both to &lt;code&gt;public/index.php&lt;/code&gt;. You do not run a second process and you do not open an API port.&lt;/p&gt;

&lt;p&gt;When it fails, look here first:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;storage/logs/laravel.log&lt;/code&gt; for a 500 page. Read the &lt;code&gt;production.ERROR&lt;/code&gt; line.&lt;/li&gt;
&lt;li&gt;Confirm Nginx &lt;code&gt;root&lt;/code&gt; is &lt;code&gt;.../public&lt;/code&gt;, not the project root.&lt;/li&gt;
&lt;li&gt;Confirm &lt;code&gt;DB_HOST&lt;/code&gt; is &lt;code&gt;127.0.0.1&lt;/code&gt; and that &lt;code&gt;SELECT user, host FROM mysql.user&lt;/code&gt; shows that host.&lt;/li&gt;
&lt;li&gt;After an &lt;code&gt;.env&lt;/code&gt; edit, &lt;code&gt;php artisan config:cache&lt;/code&gt; and reload PHP-FPM.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Do not put the database password, &lt;code&gt;APP_KEY&lt;/code&gt;, or the private key in git. &lt;code&gt;deploy&lt;/code&gt; is the only account GitHub should log in as.&lt;/p&gt;

</description>
      <category>contabo</category>
      <category>github</category>
      <category>devops</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How to Point a Subdomain to Your Contabo VPS</title>
      <dc:creator>Sospeter Mong'are</dc:creator>
      <pubDate>Mon, 14 Sep 2026 19:33:31 +0000</pubDate>
      <link>https://dev.to/msnmongare/how-to-point-a-subdomain-to-your-contabo-vps-2p7g</link>
      <guid>https://dev.to/msnmongare/how-to-point-a-subdomain-to-your-contabo-vps-2p7g</guid>
      <description>&lt;p&gt;If you have a domain hosted with one provider but your application or website is running on a VPS from another provider, you don't need to move the entire domain.&lt;/p&gt;

&lt;p&gt;You can simply create a &lt;strong&gt;subdomain&lt;/strong&gt; and point it to your VPS.&lt;/p&gt;

&lt;p&gt;For example, suppose your main domain is:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;mydomain.com&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;And you have a Contabo VPS where you want to host an application at:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;mysubdomain.mydomain.com&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;In this setup:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your main domain remains with your existing DNS provider, such as Truehost.&lt;/li&gt;
&lt;li&gt;Your application runs on the Contabo VPS.&lt;/li&gt;
&lt;li&gt;The subdomain points directly to the VPS IP address.&lt;/li&gt;
&lt;li&gt;Your DNS provider tells browsers where to find that subdomain.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This guide walks through the process step by step.&lt;/p&gt;

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

&lt;p&gt;You will need:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A registered domain, for example &lt;code&gt;mydomain.com&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Access to the domain's DNS management panel&lt;/li&gt;
&lt;li&gt;A Contabo VPS&lt;/li&gt;
&lt;li&gt;The public IPv4 address of your VPS&lt;/li&gt;
&lt;li&gt;Access to the server so you can configure your web server&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In this example, we'll use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Domain: mydomain.com
Subdomain: mysubdomain.mydomain.com
VPS Provider: Contabo
DNS Provider: Truehost
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How DNS Works in This Setup
&lt;/h2&gt;

&lt;p&gt;Before changing anything, it helps to understand what is actually happening.&lt;/p&gt;

&lt;p&gt;When someone enters:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;their browser needs to know which server should receive the request.&lt;/p&gt;

&lt;p&gt;The DNS record provides that information.&lt;/p&gt;

&lt;p&gt;You will essentially tell DNS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mysubdomain.mydomain.com
        ↓
Contabo VPS IP address
        ↓
Your application
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is done using an &lt;strong&gt;A record&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;An A record maps a domain or subdomain to an IPv4 address.&lt;/p&gt;

&lt;p&gt;For more background, see &lt;a href="https://support.cpanel.net/hc/en-us/community/posts/19146524060183-How-to-point-a-subdomain-to-another-server?utm_source=chatgpt.com" rel="noopener noreferrer"&gt;cPanel's explanation of pointing a subdomain to another server&lt;/a&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  Step 1: Find Your Contabo VPS IP Address
&lt;/h1&gt;

&lt;p&gt;First, you need to find the public IP address of your Contabo VPS.&lt;/p&gt;

&lt;p&gt;Log in to your &lt;strong&gt;Contabo Customer Control Panel&lt;/strong&gt; and open your VPS details.&lt;/p&gt;

&lt;p&gt;You should see an IPv4 address similar to:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Copy this IP address.&lt;/p&gt;

&lt;p&gt;You will need it when creating the DNS record.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why do you need the IP address?
&lt;/h3&gt;

&lt;p&gt;DNS needs to know where to send requests for your subdomain.&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;mysubdomain.mydomain.com
              ↓
       123.123.123.123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The IP address is therefore the destination for your subdomain.&lt;/p&gt;




&lt;h1&gt;
  
  
  Step 2: Open Your DNS Zone Editor
&lt;/h1&gt;

&lt;p&gt;If your domain is managed through Truehost, log in to your Truehost cPanel.&lt;/p&gt;

&lt;p&gt;From the cPanel dashboard, look for the &lt;strong&gt;Domains&lt;/strong&gt; section.&lt;/p&gt;

&lt;p&gt;Select:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Zone Editor&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The Zone Editor allows you to manage DNS records for your domain.&lt;/p&gt;

&lt;p&gt;You can also refer to &lt;a href="https://truehost.com/subdomain-vs-parked-vs-addon-domain/?utm_source=chatgpt.com" rel="noopener noreferrer"&gt;Truehost's guide on subdomains and domain configuration&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You should see your domain listed, 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;mydomain.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Select the option to manage its DNS records.&lt;/p&gt;




&lt;h1&gt;
  
  
  Step 3: Create an A Record
&lt;/h1&gt;

&lt;p&gt;This is the most important step.&lt;/p&gt;

&lt;p&gt;An &lt;strong&gt;A record&lt;/strong&gt; tells DNS:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When someone requests this hostname, send them to this IPv4 address.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Next to &lt;code&gt;mydomain.com&lt;/code&gt;, select:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;+ A Record&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You will typically see fields similar to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Name:
Address / Value:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enter your values.&lt;/p&gt;

&lt;h3&gt;
  
  
  Name
&lt;/h3&gt;

&lt;p&gt;You can enter:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Depending on your DNS provider, you may also be able to enter the complete hostname:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mysubdomain.mydomain.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Address / Value
&lt;/h3&gt;

&lt;p&gt;Enter the IPv4 address of your Contabo VPS.&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;123.123.123.123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your record will effectively look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Type:     A
Name:     mysubdomain
Value:    123.123.123.123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then save the record.&lt;/p&gt;

&lt;p&gt;For additional background on creating A records, see &lt;a href="https://www.hostpapa.com/knowledgebase/add-subdomain-points-ip-address/?utm_source=chatgpt.com" rel="noopener noreferrer"&gt;HostPapa's guide to pointing a subdomain to an IP address&lt;/a&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  Step 4: Configure Your Contabo VPS
&lt;/h1&gt;

&lt;p&gt;Adding the DNS record is only half of the process.&lt;/p&gt;

&lt;p&gt;Your VPS also needs to know what to do when a request arrives for:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mysubdomain.mydomain.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example, if you're using &lt;strong&gt;Nginx&lt;/strong&gt;, you need to configure a server block for the subdomain.&lt;/p&gt;

&lt;p&gt;A simplified configuration could look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;mysubdomain.mydomain.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;root&lt;/span&gt; &lt;span class="n"&gt;/var/www/mysubdomain&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;index&lt;/span&gt; &lt;span class="s"&gt;index.html&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;If you're using Apache, CyberPanel, or another server management solution, the configuration will be different.&lt;/p&gt;

&lt;p&gt;The important thing is that your VPS must be configured to recognize the hostname.&lt;/p&gt;

&lt;p&gt;Think of it this way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DNS
 |
 | mysubdomain.mydomain.com
 ↓
Contabo VPS
 |
 | "I know this domain"
 ↓
Web server
 |
 ↓
Your application
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If DNS is configured correctly but your web server doesn't recognize the domain, the request can still fail.&lt;/p&gt;




&lt;h1&gt;
  
  
  Step 5: Test the DNS Configuration
&lt;/h1&gt;

&lt;p&gt;After saving the A record, you can check whether the DNS record is resolving correctly.&lt;/p&gt;

&lt;p&gt;From your computer, you can use:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dig mysubdomain.mydomain.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should eventually see your Contabo VPS IP address returned.&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;mysubdomain.mydomain.com
Address: 123.123.123.123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also use online DNS lookup tools to check whether the record has propagated.&lt;/p&gt;




&lt;h1&gt;
  
  
  Step 6: Configure HTTPS
&lt;/h1&gt;

&lt;p&gt;Once the subdomain is pointing correctly to your VPS, you will probably want to enable HTTPS.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://mysubdomain.mydomain.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;you want:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;If you're using Nginx, one popular option is &lt;strong&gt;Certbot&lt;/strong&gt; with Let's Encrypt.&lt;/p&gt;

&lt;p&gt;Let's Encrypt provides free TLS certificates for HTTPS.&lt;/p&gt;

&lt;p&gt;You can learn more from &lt;a href="https://letsencrypt.org/docs/?utm_source=chatgpt.com" rel="noopener noreferrer"&gt;the official Let's Encrypt documentation&lt;/a&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  How Long Does DNS Propagation Take?
&lt;/h1&gt;

&lt;p&gt;DNS changes are not always visible immediately.&lt;/p&gt;

&lt;p&gt;After creating the A record, it can take some time before different DNS resolvers around the internet start returning the new IP address.&lt;/p&gt;

&lt;p&gt;You may see the subdomain working within a few minutes, while in some cases you may need to wait longer.&lt;/p&gt;

&lt;p&gt;This is commonly referred to as &lt;strong&gt;DNS propagation&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So don't worry if:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Your computer → old DNS information
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;while another device or DNS checker already sees:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mysubdomain.mydomain.com → Contabo VPS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;DNS caching can cause temporary differences.&lt;/p&gt;




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

&lt;p&gt;If your subdomain isn't working, check these things first.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Wrong IP address
&lt;/h3&gt;

&lt;p&gt;Make sure the A record points to the public IPv4 address of your Contabo VPS.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Incorrect DNS record
&lt;/h3&gt;

&lt;p&gt;Make sure you created an &lt;strong&gt;A record&lt;/strong&gt;, not a CNAME record, if you're directly pointing the hostname to an IPv4 address.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Web server isn't configured
&lt;/h3&gt;

&lt;p&gt;DNS can successfully point the domain to your VPS, but Nginx, Apache, or your control panel still needs to know how to handle the hostname.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Firewall
&lt;/h3&gt;

&lt;p&gt;Make sure your VPS firewall allows HTTP and HTTPS traffic.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Port 80  - HTTP
Port 443 - HTTPS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. DNS hasn't propagated yet
&lt;/h3&gt;

&lt;p&gt;Give the DNS change some time and check it using &lt;code&gt;nslookup&lt;/code&gt;, &lt;code&gt;dig&lt;/code&gt;, or an online DNS checker.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Final Setup
&lt;/h1&gt;

&lt;p&gt;Once everything is configured, the architecture will 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;                    Internet
                       |
                       |
        mysubdomain.mydomain.com
                       |
                       ↓
              DNS A Record
                       |
                       ↓
             Contabo VPS IP
              123.123.123.123
                       |
                       ↓
                Nginx / Apache
                       |
                       ↓
                Your Application
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your main domain can continue operating normally:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;while your subdomain runs from the Contabo VPS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mysubdomain.mydomain.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You don't need to move the entire domain to Contabo just to host one application on your VPS.&lt;/p&gt;

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

&lt;p&gt;Pointing a subdomain to a Contabo VPS is relatively straightforward once you understand the separation between &lt;strong&gt;DNS and your server&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The basic process is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Find your Contabo VPS IP address.&lt;/li&gt;
&lt;li&gt;Open your domain's DNS Zone Editor.&lt;/li&gt;
&lt;li&gt;Create an A record for your subdomain.&lt;/li&gt;
&lt;li&gt;Point the A record to the VPS IP address.&lt;/li&gt;
&lt;li&gt;Configure Nginx, Apache, or your server software to handle the subdomain.&lt;/li&gt;
&lt;li&gt;Wait for DNS changes to propagate.&lt;/li&gt;
&lt;li&gt;Configure HTTPS once the domain resolves correctly.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The key thing to remember is that &lt;strong&gt;DNS only tells the internet where your server is&lt;/strong&gt;. Your VPS still needs to be configured to serve the application for that specific hostname.&lt;/p&gt;

&lt;p&gt;Once you understand this pattern, you can use it to host multiple applications on the same VPS, 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;api.mydomain.com
app.mydomain.com
admin.mydomain.com
blog.mydomain.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All of them can point to the same VPS while your web server routes each hostname to the appropriate application.&lt;/p&gt;

</description>
      <category>linux</category>
      <category>containers</category>
      <category>contabo</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Getting Started with Dagster: A Beginner's Guide</title>
      <dc:creator>Sospeter Mong'are</dc:creator>
      <pubDate>Wed, 09 Sep 2026 14:17:58 +0000</pubDate>
      <link>https://dev.to/msnmongare/getting-started-with-dagster-a-beginners-guide-21f</link>
      <guid>https://dev.to/msnmongare/getting-started-with-dagster-a-beginners-guide-21f</guid>
      <description>&lt;p&gt;Welcome! Let's create a simple Dagster project step by step. 🎉&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: Scaffold a New Dagster Project
&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;pip &lt;span class="nb"&gt;install &lt;/span&gt;create-dagster
create-dagster project my-first-dagster-project
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then navigate into your project:&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;my-first-dagster-project
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://docs.dagster.io/dagster-basics-tutorial/projects" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: Set Up Your Virtual Environment
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; venv .venv
&lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--editable&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://docs.dagster.io/dagster-basics-tutorial/projects" rel="noopener noreferrer"&gt;2&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: Your Project Structure
&lt;/h2&gt;

&lt;p&gt;Your project should look 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;.
├── pyproject.toml
├── README.md
├── src
│   └── my_first_dagster_project
│       ├── __init__.py
│       ├── definitions.py
│       └── defs
│           └── __init__.py
└── tests
    └── __init__.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://docs.dagster.io/dagster-basics-tutorial/projects" rel="noopener noreferrer"&gt;3&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4: Create Your First Asset
&lt;/h2&gt;

&lt;p&gt;Inside &lt;code&gt;src/my_first_dagster_project/defs/&lt;/code&gt;, create a file called &lt;code&gt;assets.py&lt;/code&gt; and add this simple code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dagster&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;dg&lt;/span&gt;

&lt;span class="nd"&gt;@dg.asset&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;my_first_asset&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="s"&gt;Hello from my first Dagster asset!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nd"&gt;@dg.asset&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;my_second_asset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_first_asset&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_first_asset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;The sum is: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&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;total&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 5: Register Your Assets in &lt;code&gt;definitions.py&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Open &lt;code&gt;src/my_first_dagster_project/definitions.py&lt;/code&gt; and update it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dagster&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;dg&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;my_first_dagster_project.defs.assets&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;my_first_asset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;my_second_asset&lt;/span&gt;

&lt;span class="n"&gt;defs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Definitions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;assets&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;my_first_asset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;my_second_asset&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 6: Launch the Dagster UI
&lt;/h2&gt;

&lt;p&gt;Run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dg dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then open your browser and go to:&lt;br&gt;
👉 &lt;strong&gt;&lt;a href="http://127.0.0.1:3000" rel="noopener noreferrer"&gt;http://127.0.0.1:3000&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.dagster.io/dagster-basics-tutorial/projects" rel="noopener noreferrer"&gt;4&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 7: Materialize Your Assets
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;In the Dagster UI, click on &lt;strong&gt;Assets&lt;/strong&gt; in the top navigation&lt;/li&gt;
&lt;li&gt;You should see &lt;code&gt;my_first_asset&lt;/code&gt; and &lt;code&gt;my_second_asset&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Materialize All&lt;/strong&gt; to run them&lt;/li&gt;
&lt;li&gt;Watch them execute and check the logs! ✅&lt;/li&gt;
&lt;/ol&gt;


&lt;h2&gt;
  
  
  What Just Happened? 🤔
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;my_first_asset&lt;/code&gt;&lt;/strong&gt; runs first and returns a list of numbers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;my_second_asset&lt;/code&gt;&lt;/strong&gt; depends on &lt;code&gt;my_first_asset&lt;/code&gt; and calculates the sum&lt;/li&gt;
&lt;li&gt;Dagster automatically figures out the &lt;strong&gt;execution order&lt;/strong&gt; based on dependencies!&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;&lt;strong&gt;Question&lt;/strong&gt;: Should I be running this everytime, Even though I installed Dagster?&lt;/p&gt;

&lt;p&gt;Great question! &lt;strong&gt;No, you don't need to run those commands every time.&lt;/strong&gt; Here's what each command does and when you need it:&lt;/p&gt;


&lt;h2&gt;
  
  
  One-Time Commands (Run Only Once)
&lt;/h2&gt;

&lt;p&gt;These are &lt;strong&gt;setup commands&lt;/strong&gt; you only run when creating a &lt;strong&gt;new project&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;create-dagster                        &lt;span class="c"&gt;# Installs the create-dagster tool&lt;/span&gt;
create-dagster project my-first-dagster-project   &lt;span class="c"&gt;# Creates your project folder&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Think of it like &lt;strong&gt;creating a new folder/workspace&lt;/strong&gt; — you only do it once! &lt;a href="https://docs.dagster.io/getting-started/installation" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Every Time You Work on Your Project
&lt;/h2&gt;

&lt;p&gt;These are the commands you run &lt;strong&gt;each time&lt;/strong&gt; you open a new terminal session:&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;# 1. Navigate to your project&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;my-first-dagster-project

&lt;span class="c"&gt;# 2. Activate your virtual environment (REQUIRED every new terminal session)&lt;/span&gt;
&lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate

&lt;span class="c"&gt;# 3. Start the Dagster UI&lt;/span&gt;
dg dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://docs.dagster.io/dagster-basics-tutorial/projects" rel="noopener noreferrer"&gt;2&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Quick Summary Table
&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;When to Run&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pip install create-dagster&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅ Once ever&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;create-dagster project my-project&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅ Once per new project&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;python -m venv .venv&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅ Once per new project&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;source .venv/bin/activate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;🔁 Every new terminal session&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dg dev&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;🔁 Every time you want to use the UI&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Pro Tip 💡
&lt;/h2&gt;

&lt;p&gt;Since you're on &lt;strong&gt;WSL (Windows)&lt;/strong&gt;, every time you open a new terminal, just 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;cd &lt;/span&gt;my-first-dagster-project
&lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate
dg dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And you're good to go! 🚀&lt;/p&gt;

&lt;p&gt;You're now running your first Dagster pipeline! Feel free to ask if you have any questions. 🚀&lt;/p&gt;

</description>
      <category>database</category>
      <category>ai</category>
    </item>
    <item>
      <title>How to Install Dagster: WSL &amp; Native Windows Guide for Beginners (Using pip)</title>
      <dc:creator>Sospeter Mong'are</dc:creator>
      <pubDate>Wed, 09 Sep 2026 14:16:18 +0000</pubDate>
      <link>https://dev.to/msnmongare/how-to-install-dagster-wsl-native-windows-guide-for-beginners-using-pip-5cjc</link>
      <guid>https://dev.to/msnmongare/how-to-install-dagster-wsl-native-windows-guide-for-beginners-using-pip-5cjc</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Dagster is a modern data orchestration platform that helps you build, schedule, and monitor data pipelines. In this guide, we'll walk through two ways to install Dagster on a Windows machine using &lt;strong&gt;pip&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Via WSL (Windows Subsystem for Linux)&lt;/strong&gt; - Recommended&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Directly on Native Windows&lt;/strong&gt; - Without WSL&lt;/li&gt;
&lt;/ol&gt;




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

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Python 3.10 or higher&lt;/strong&gt; installed (&lt;strong&gt;Python 3.13 is recommended&lt;/strong&gt;) &lt;a href="https://docs.dagster.io/getting-started/installation" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A stable internet connection&lt;/li&gt;
&lt;li&gt;Basic familiarity with the terminal/command prompt&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Method 1: Installing Dagster via WSL (Recommended) 🐧
&lt;/h1&gt;

&lt;p&gt;WSL (Windows Subsystem for Linux) lets you run a Linux environment directly on Windows. This is the &lt;strong&gt;recommended approach&lt;/strong&gt; because Dagster works best in a Linux-like environment.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: Install WSL
&lt;/h2&gt;

&lt;p&gt;Open &lt;strong&gt;PowerShell as Administrator&lt;/strong&gt; 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;This will install WSL with &lt;strong&gt;Ubuntu&lt;/strong&gt; by default. Restart your computer when prompted.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: Set Up Ubuntu
&lt;/h2&gt;

&lt;p&gt;After restarting:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;strong&gt;Ubuntu&lt;/strong&gt; from the Start Menu&lt;/li&gt;
&lt;li&gt;Create a username and password when prompted&lt;/li&gt;
&lt;li&gt;Update your packages:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="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;h2&gt;
  
  
  Step 3: Install Python
&lt;/h2&gt;

&lt;p&gt;Check if Python is already installed:&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;If not installed or version is below 3.10, 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 &lt;span class="nb"&gt;install &lt;/span&gt;python3 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 4: Create Your Dagster Project Folder
&lt;/h2&gt;

&lt;p&gt;Create and navigate into a project folder:&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;my-dagster-project
&lt;span class="nb"&gt;cd &lt;/span&gt;my-dagster-project
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 5: Create a Virtual Environment
&lt;/h2&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;h2&gt;
  
  
  Step 6: Activate the Virtual Environment
&lt;/h2&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;You should see &lt;code&gt;(.venv)&lt;/code&gt; appear at the beginning of your terminal line. ✅&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 7: Install Dagster Using pip
&lt;/h2&gt;

&lt;p&gt;Now install Dagster and its dependencies: &lt;a href="https://docs.dagster.io/getting-started/installation" rel="noopener noreferrer"&gt;2&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;dagster dagster-webserver dagster-dg-cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This installs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;dagster&lt;/code&gt;&lt;/strong&gt; - The core Dagster library&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;dagster-webserver&lt;/code&gt;&lt;/strong&gt; - The Dagster UI&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;dagster-dg-cli&lt;/code&gt;&lt;/strong&gt; - The &lt;code&gt;dg&lt;/code&gt; command line tool&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 8: Scaffold a Dagster Project
&lt;/h2&gt;

&lt;p&gt;Use the &lt;code&gt;create-dagster&lt;/code&gt; CLI to scaffold a new project: &lt;a href="https://docs.dagster.io/getting-started/installation" rel="noopener noreferrer"&gt;3&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;create-dagster project my-project
&lt;span class="nb"&gt;cd &lt;/span&gt;my-project
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 9: Install Project Dependencies
&lt;/h2&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;--editable&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--group&lt;/span&gt; dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The &lt;code&gt;--group&lt;/code&gt; argument requires pip 25.1 or higher. If you have an older version, upgrade pip first: &lt;a href="https://docs.dagster.io/getting-started/installation" rel="noopener noreferrer"&gt;4&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&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 10: Verify the Installation
&lt;/h2&gt;



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

&lt;/div&gt;



&lt;p&gt;You should see the version number printed in your terminal. &lt;a href="https://docs.dagster.io/getting-started/installation" rel="noopener noreferrer"&gt;5&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 11: Launch Dagster
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dg dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open your browser and go to 👉 &lt;strong&gt;&lt;a href="http://127.0.0.1:3000" rel="noopener noreferrer"&gt;http://127.0.0.1:3000&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Every Time You Return (WSL) 🔁
&lt;/h2&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;my-dagster-project/my-project
&lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate
dg dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;









&lt;h1&gt;
  
  
  Method 2: Installing Dagster on Native Windows (Without WSL) 🪟
&lt;/h1&gt;

&lt;p&gt;If you prefer not to use WSL, you can install Dagster directly on Windows using &lt;strong&gt;Command Prompt or PowerShell&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: Install Python
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;a href="https://www.python.org/downloads/" rel="noopener noreferrer"&gt;python.org/downloads&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Download &lt;strong&gt;Python 3.13&lt;/strong&gt; (recommended) &lt;a href="https://docs.dagster.io/getting-started/installation" rel="noopener noreferrer"&gt;6&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Run the installer&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Important:&lt;/strong&gt; Check &lt;strong&gt;"Add Python to PATH"&lt;/strong&gt; during installation&lt;/li&gt;
&lt;/ol&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 powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--version&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






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

&lt;p&gt;Make sure you have the latest version of pip: &lt;a href="https://docs.dagster.io/getting-started/installation" rel="noopener noreferrer"&gt;7&lt;/a&gt;&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;pip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--upgrade&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pip&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 3: Create Your Dagster Project Folder
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;mkdir&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;my-dagster-project&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;my-dagster-project&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






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



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;venv&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;venv&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: Activate the Virtual Environment
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;venv&lt;/span&gt;&lt;span class="n"&gt;\Scripts\activate&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see &lt;code&gt;(.venv)&lt;/code&gt; appear at the beginning of your terminal line. ✅&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 6: Install Dagster Using pip
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;pip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dagster&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dagster-webserver&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dagster-dg-cli&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://docs.dagster.io/getting-started/installation" rel="noopener noreferrer"&gt;8&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 7: Scaffold a Dagster Project
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;create-dagster&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;project&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;my-project&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;my-project&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 8: Install Project Dependencies
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;pip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--editable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--group&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dev&lt;/span&gt;&lt;span class="w"&gt;
&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; The &lt;code&gt;--group&lt;/code&gt; argument requires &lt;strong&gt;pip 25.1 or higher&lt;/strong&gt;. If you have an older version, omit &lt;code&gt;--group dev&lt;/code&gt; and install the CLI separately: &lt;a href="https://docs.dagster.io/getting-started/installation" rel="noopener noreferrer"&gt;9&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;pip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dagster-dg-cli&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 9: Verify the Installation
&lt;/h2&gt;



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

&lt;/div&gt;



&lt;p&gt;You should see the version number of &lt;code&gt;dg&lt;/code&gt; printed. &lt;a href="https://docs.dagster.io/getting-started/installation" rel="noopener noreferrer"&gt;10&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 10: Launch Dagster
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;dg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dev&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open your browser and go to 👉 &lt;strong&gt;&lt;a href="http://127.0.0.1:3000" rel="noopener noreferrer"&gt;http://127.0.0.1:3000&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Every Time You Return (Native Windows) 🔁
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;my-dagster-project\my-project&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;venv&lt;/span&gt;&lt;span class="n"&gt;\Scripts\activate&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nx"&gt;dg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dev&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;









&lt;h1&gt;
  
  
  WSL vs Native Windows: Which Should You Choose?
&lt;/h1&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Factor&lt;/th&gt;
&lt;th&gt;WSL 🐧&lt;/th&gt;
&lt;th&gt;Native Windows 🪟&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Recommended by Dagster&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;⚠️ Works but less common&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ease of Setup&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Moderate&lt;/td&gt;
&lt;td&gt;Easy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Performance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Better&lt;/td&gt;
&lt;td&gt;Good&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Linux Commands&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Supported&lt;/td&gt;
&lt;td&gt;❌ Not supported&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Community Support&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ More resources&lt;/td&gt;
&lt;td&gt;⚠️ Limited&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Best For&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Developers &amp;amp; Data Engineers&lt;/td&gt;
&lt;td&gt;Beginners on Windows&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h1&gt;
  
  
  Troubleshooting Common Issues 🔧
&lt;/h1&gt;

&lt;h3&gt;
  
  
  ❌ &lt;code&gt;dg&lt;/code&gt; command not found
&lt;/h3&gt;

&lt;p&gt;Make sure your virtual environment is activated:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;WSL: &lt;code&gt;source .venv/bin/activate&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Windows: &lt;code&gt;.venv\Scripts\activate&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ❌ Python version too old
&lt;/h3&gt;

&lt;p&gt;Make sure you have &lt;strong&gt;Python 3.10+&lt;/strong&gt; installed. &lt;a href="https://docs.dagster.io/getting-started/installation" rel="noopener noreferrer"&gt;11&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ pip version too old
&lt;/h3&gt;

&lt;p&gt;Upgrade pip with:&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;h3&gt;
  
  
  ❌ Port 3000 already in use
&lt;/h3&gt;

&lt;p&gt;Run Dagster 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;dg dev &lt;span class="nt"&gt;-p&lt;/span&gt; 8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ❌ Still stuck?
&lt;/h3&gt;

&lt;p&gt;Reach out to the &lt;strong&gt;Dagster community&lt;/strong&gt; for help at &lt;a href="https://dagster.io/community" rel="noopener noreferrer"&gt;dagster.io/community&lt;/a&gt; &lt;a href="https://docs.dagster.io/getting-started/installation" rel="noopener noreferrer"&gt;12&lt;/a&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Summary
&lt;/h1&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;WSL 🐧&lt;/th&gt;
&lt;th&gt;Native Windows 🪟&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Install Python&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo apt install python3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Download from python.org&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Create venv&lt;/td&gt;
&lt;td&gt;&lt;code&gt;python3 -m venv .venv&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;python -m venv .venv&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Activate venv&lt;/td&gt;
&lt;td&gt;&lt;code&gt;source .venv/bin/activate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.venv\Scripts\activate&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Install Dagster&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pip install dagster dagster-webserver dagster-dg-cli&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pip install dagster dagster-webserver dagster-dg-cli&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Launch UI&lt;/td&gt;
&lt;td&gt;&lt;code&gt;dg dev&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;dg dev&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;p&gt;You are now ready to start building amazing data pipelines with Dagster! 🚀&lt;/p&gt;

&lt;p&gt;Happy orchestrating! 🎉&lt;/p&gt;

</description>
      <category>powerplatform</category>
      <category>data</category>
      <category>dataengineering</category>
    </item>
    <item>
      <title>Running AI Models Locally on Windows</title>
      <dc:creator>Sospeter Mong'are</dc:creator>
      <pubDate>Sat, 05 Sep 2026 04:39:25 +0000</pubDate>
      <link>https://dev.to/msnmongare/running-ai-models-locally-on-windows-a-beginners-guide-3n3l</link>
      <guid>https://dev.to/msnmongare/running-ai-models-locally-on-windows-a-beginners-guide-3n3l</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;What if you could run your own AI assistant - one that works without the internet, costs nothing per message, never shares your data with anyone, and is available any time you need it? No subscriptions. No monthly limits. No worrying about what happens to your conversations.&lt;/p&gt;

&lt;p&gt;That is exactly what running AI models locally means. Thanks to a free tool called &lt;strong&gt;Ollama&lt;/strong&gt;, you can download and run powerful AI language models directly on your Windows laptop or desktop - the same way you would install any other application.&lt;/p&gt;

&lt;p&gt;This guide will walk you through everything from scratch. No prior experience required. By the end, you will have your own AI assistant running on your machine, accessible both from the terminal and from a full browser-based chat interface that looks and feels just like ChatGPT.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Would You Want to Do This?
&lt;/h2&gt;

&lt;p&gt;If ChatGPT and Claude already exist, why bother running something locally? Here are the real reasons people do it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Complete Privacy&lt;/strong&gt;&lt;br&gt;
When you type into ChatGPT or Claude, your messages are sent to servers owned by OpenAI or Anthropic. Those companies may use your conversations to improve their models, and your data lives on their infrastructure. With a local model, your prompts never leave your computer. Not one word. This matters enormously when dealing with sensitive work documents, client data, medical information, or anything confidential.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Zero Cost&lt;/strong&gt;&lt;br&gt;
ChatGPT Plus costs around $20 per month. Claude Pro is similar. API usage costs money per request. Local models cost absolutely nothing after the initial download. You can run millions of messages and the bill stays at zero.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Works Completely Offline&lt;/strong&gt;&lt;br&gt;
Flying with no Wi-Fi? In a location with poor internet? Your local model does not care. Once downloaded, it runs entirely from your machine. No network required.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. No Rate Limits or Message Caps&lt;/strong&gt;&lt;br&gt;
Cloud AI services throttle heavy users. You hit a limit, you wait. Local models have no such restriction. Run them as hard as you want for as long as you want.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Always Available&lt;/strong&gt;&lt;br&gt;
No service outages, no maintenance windows, no "ChatGPT is at capacity right now." Your model is there whenever you open your terminal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Learning and Experimentation&lt;/strong&gt;&lt;br&gt;
Developers, researchers, and curious professionals use local models to experiment with AI, test prompts, build applications, and understand how these systems work - all without racking up API costs.&lt;/p&gt;


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

&lt;p&gt;You do not need a powerful gaming machine. Here is what matters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Operating System:&lt;/strong&gt; Windows 10 or Windows 11&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RAM:&lt;/strong&gt; 8 GB minimum, 16 GB recommended&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Free Disk Space:&lt;/strong&gt; At least 5 GB, ideally 15 GB or more&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GPU:&lt;/strong&gt; Not required - models run on your CPU&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Internet:&lt;/strong&gt; Only needed for the initial download&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;RAM is the most important factor. The more you have, the better the model you can run:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;8 GB RAM - run 1B to 2B models. Fast but basic.&lt;/li&gt;
&lt;li&gt;16 GB RAM - run 2B to 4B models. Good balance of speed and quality.&lt;/li&gt;
&lt;li&gt;32 GB RAM - run 7B to 13B models. Noticeably more capable.&lt;/li&gt;
&lt;li&gt;48 GB RAM or more - run 70B models. Closest to GPT-4 quality locally.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Understanding Model Sizes
&lt;/h2&gt;

&lt;p&gt;When you see terms like "2B" or "7B", the B stands for &lt;strong&gt;billion parameters&lt;/strong&gt;. Parameters are the internal numbers that make up the model's knowledge and reasoning ability. More parameters generally means more capable responses, larger file size, more RAM required, and slower responses on CPU.&lt;/p&gt;

&lt;p&gt;Think of it like engine size in a car. A 2B model is a small efficient engine - fast and economical. A 70B model is a high-performance engine - far more powerful but needs much more fuel (RAM) and takes longer to get going. For most everyday tasks on a standard laptop, a 3B to 7B model hits the sweet spot perfectly.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Models Available on Ollama and What They Are Best At
&lt;/h2&gt;

&lt;p&gt;Models come in different sizes. The bigger the parameter count, the more capable but the more RAM and time required. Here is a full breakdown from smallest to largest.&lt;/p&gt;


&lt;h3&gt;
  
  
  Small Models - 1B to 4B Parameters
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Best for: laptops with 8–16 GB RAM, everyday tasks, fast responses&lt;/em&gt;&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;gemma2:2b - Google Gemma 2 (2 Billion)&lt;/strong&gt;&lt;br&gt;
Size: ~1.6 GB | RAM needed: 4–6 GB&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama pull gemma2:2b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Google's lightweight model. The fastest option on CPU. Ideal for quick questions, short summaries, and simple drafts. Not suited for complex reasoning but excellent when speed matters more than depth.&lt;/p&gt;

&lt;p&gt;Best used for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Quick factual questions&lt;/li&gt;
&lt;li&gt;Summarising short pieces of text&lt;/li&gt;
&lt;li&gt;Simple email drafting&lt;/li&gt;
&lt;li&gt;When you need a fast answer and don't need deep reasoning&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;phi3:mini - Microsoft Phi-3 Mini (3.8 Billion)&lt;/strong&gt;&lt;br&gt;
Size: ~2.2 GB | RAM needed: 6–8 GB&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama pull phi3:mini
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Trained on high-quality textbook-style content. Punches well above its weight on structured tasks. Best small model for code, JSON output, and step-by-step reasoning. If you are a developer this should be your first download.&lt;/p&gt;

&lt;p&gt;Best used for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Coding questions and debugging&lt;/li&gt;
&lt;li&gt;Structured outputs like JSON or formatted data&lt;/li&gt;
&lt;li&gt;Step-by-step explanations&lt;/li&gt;
&lt;li&gt;Maths and logic problems&lt;/li&gt;
&lt;li&gt;Following detailed instructions precisely&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;llama3.2:3b - Meta Llama 3.2 (3 Billion)&lt;/strong&gt;&lt;br&gt;
Size: ~2.0 GB | RAM needed: 6–8 GB&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama pull llama3.2:3b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Meta's latest small model and the best all-rounder in the 3B range. Handles a wide variety of tasks reliably. This is the model most people should start with.&lt;/p&gt;

&lt;p&gt;Best used for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;General conversation and everyday questions&lt;/li&gt;
&lt;li&gt;Writing assistance - emails, reports, summaries&lt;/li&gt;
&lt;li&gt;Explaining complex topics in plain language&lt;/li&gt;
&lt;li&gt;Brainstorming and idea generation&lt;/li&gt;
&lt;li&gt;Basic coding help&lt;/li&gt;
&lt;li&gt;Translation and language tasks&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;qwen2.5:3b - Alibaba Qwen 2.5 (3 Billion)&lt;/strong&gt;&lt;br&gt;
Size: ~1.9 GB | RAM needed: 6–8 GB&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama pull qwen2.5:3b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alibaba's small model with exceptional multilingual capability and reliable structured output. If you work in multiple languages or need consistent JSON and formatted data, this is your pick at the small end.&lt;/p&gt;

&lt;p&gt;Best used for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Structured data extraction&lt;/li&gt;
&lt;li&gt;JSON and formatted output tasks&lt;/li&gt;
&lt;li&gt;Multilingual tasks - works well in multiple languages including non-English ones&lt;/li&gt;
&lt;li&gt;Data classification and categorisation&lt;/li&gt;
&lt;li&gt;Anything where consistent output format matters&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;mistral:7b - Mistral AI (7 Billion)&lt;/strong&gt;&lt;br&gt;
Size: ~4.1 GB | RAM needed: 8–12 GB&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama pull mistral:7b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One of the most popular open source models ever released. Mistral 7B outperforms many models twice its size. Excellent all-rounder that handles nuanced writing, reasoning, and coding far better than the 3B models. If your machine has 16 GB RAM, start here instead of the smaller models. The quality jump from 3B to 7B is very noticeable.&lt;/p&gt;

&lt;p&gt;Best used for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Everything llama3.2:3b does, but better&lt;/li&gt;
&lt;li&gt;Nuanced writing with more depth and flow&lt;/li&gt;
&lt;li&gt;Coding with fewer errors&lt;/li&gt;
&lt;li&gt;Longer documents where context matters&lt;/li&gt;
&lt;li&gt;When you want noticeably better quality and have the RAM&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Medium Models - 7B to 13B Parameters
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Best for: machines with 16 GB RAM, more complex tasks, better reasoning&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;llama3.1:8b - Meta Llama 3.1 (8 Billion)&lt;/strong&gt;&lt;br&gt;
Size: ~4.7 GB | RAM needed: 10–14 GB&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama pull llama3.1:8b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One of Meta's strongest open source releases. At 8B parameters it handles complex instructions, long documents, nuanced writing, and multi-step reasoning much better than the 3B version. This is the sweet spot for most people with 16 GB RAM - capable enough for serious work, still manageable on CPU.&lt;/p&gt;

&lt;p&gt;Best used for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Complex multi-step tasks and analysis&lt;/li&gt;
&lt;li&gt;Long document summarisation&lt;/li&gt;
&lt;li&gt;Detailed writing with nuance&lt;/li&gt;
&lt;li&gt;Research assistance&lt;/li&gt;
&lt;li&gt;Technical explanations&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;gemma2:9b - Google Gemma 2 (9 Billion)&lt;/strong&gt;&lt;br&gt;
Size: ~5.4 GB | RAM needed: 12–16 GB&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama pull gemma2:9b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The bigger sibling of gemma2:2b. Substantially more capable while still being efficient. Strong at instruction following, summarisation of long texts, and factual question answering. Google specifically optimised this size for local deployment.&lt;/p&gt;

&lt;p&gt;Best used for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Summarising long reports and documents&lt;/li&gt;
&lt;li&gt;Factual question answering with better accuracy&lt;/li&gt;
&lt;li&gt;Instruction-following tasks requiring precision&lt;/li&gt;
&lt;li&gt;When you want Google-quality output locally&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;deepseek-coder:6.7b - DeepSeek Coder (6.7 Billion)&lt;/strong&gt;&lt;br&gt;
Size: ~3.8 GB | RAM needed: 8–10 GB&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama pull deepseek-coder:6.7b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A coding-specialised model that performs remarkably well for its size. Strong at Python, JavaScript, and SQL in particular. A practical alternative to CodeLlama if you want solid code assistance without large RAM requirements.&lt;/p&gt;

&lt;p&gt;Best used for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Python, JavaScript, SQL code generation&lt;/li&gt;
&lt;li&gt;Debugging and error fixing&lt;/li&gt;
&lt;li&gt;Code explanation and documentation&lt;/li&gt;
&lt;li&gt;Writing unit tests&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;codellama:13b - Meta Code Llama (13 Billion)&lt;/strong&gt;&lt;br&gt;
Size: ~7.4 GB | RAM needed: 14–18 GB&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama pull codellama:13b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Built specifically for code. If you are a developer and code assistance is your primary use case, CodeLlama at 13B is one of the best local options available. Handles Python, JavaScript, SQL, and many other languages. Can explain existing code, generate new code, and debug errors. Not designed for general conversation.&lt;/p&gt;

&lt;p&gt;Best used for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Serious code generation across many languages&lt;/li&gt;
&lt;li&gt;Explaining and refactoring existing codebases&lt;/li&gt;
&lt;li&gt;Generating tests and documentation&lt;/li&gt;
&lt;li&gt;Complex debugging&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;phi3:medium - Microsoft Phi-3 Medium (14 Billion)&lt;/strong&gt;&lt;br&gt;
Size: ~7.9 GB | RAM needed: 14–18 GB&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama pull phi3:medium
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Microsoft's larger Phi-3 model. Takes everything phi3:mini does well - structured reasoning, code, precise instructions - and does it significantly better. Recommended for developers who need reliable code generation or anyone doing complex data tasks.&lt;/p&gt;

&lt;p&gt;Best used for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Advanced coding and structured output&lt;/li&gt;
&lt;li&gt;Complex step-by-step reasoning&lt;/li&gt;
&lt;li&gt;Data tasks requiring high precision&lt;/li&gt;
&lt;li&gt;When phi3:mini is good but not quite good enough&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;mistral-nemo:12b - Mistral AI (12 Billion)&lt;/strong&gt;&lt;br&gt;
Size: ~7.1 GB | RAM needed: 14–18 GB&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama pull mistral-nemo:12b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mistral's newer medium model built in collaboration with NVIDIA. Very strong at instruction following, long context understanding, and technical writing. A step up from mistral:7b in quality while remaining practical on a 16 GB machine.&lt;/p&gt;

&lt;p&gt;Best used for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Long context tasks where the model needs to remember a lot&lt;/li&gt;
&lt;li&gt;Technical documentation and writing&lt;/li&gt;
&lt;li&gt;Detailed instruction following&lt;/li&gt;
&lt;li&gt;Professional report drafting&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;llava:13b - LLaVA Multimodal (13 Billion)&lt;/strong&gt;&lt;br&gt;
Size: ~8.0 GB | RAM needed: 14–18 GB&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama pull llava:13b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One of the very few local models that can look at images. Send it a photo, screenshot, diagram, or chart and ask questions about it. Not as capable as GPT-4 Vision but genuinely useful and completely private. If you need local image understanding, this is your only realistic option at this size.&lt;/p&gt;

&lt;p&gt;Best used for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Asking questions about screenshots or photos&lt;/li&gt;
&lt;li&gt;Describing diagrams and charts&lt;/li&gt;
&lt;li&gt;Reading text from images&lt;/li&gt;
&lt;li&gt;Any task where you need to share a visual&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Large Models - 27B to 70B Parameters
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Best for: machines with 32 GB RAM or more, highest quality output, closest to cloud AI&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;gemma2:27b - Google Gemma 2 (27 Billion)&lt;/strong&gt;&lt;br&gt;
Size: ~16 GB | RAM needed: 20–28 GB&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama pull gemma2:27b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Google's largest Gemma model. Strong across writing, reasoning, and instruction following. Well optimised for local inference compared to other models of similar size. A good first step into large models for anyone with 32 GB RAM.&lt;/p&gt;

&lt;p&gt;Best used for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;High-quality long-form writing&lt;/li&gt;
&lt;li&gt;Complex analysis and research assistance&lt;/li&gt;
&lt;li&gt;Nuanced reasoning that smaller models struggle with&lt;/li&gt;
&lt;li&gt;Professional-grade document drafting&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;deepseek-r1:32b - DeepSeek (32 Billion)&lt;/strong&gt;&lt;br&gt;
Size: ~19 GB | RAM needed: 24–32 GB&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama pull deepseek-r1:32b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;DeepSeek's reasoning-focused model that caused a stir when released for matching or beating much larger Western models on reasoning benchmarks. Exceptional at mathematical reasoning, logic problems, and complex analytical thinking.&lt;/p&gt;

&lt;p&gt;Best used for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mathematical reasoning and problem solving&lt;/li&gt;
&lt;li&gt;Logic and analytical tasks&lt;/li&gt;
&lt;li&gt;Data interpretation&lt;/li&gt;
&lt;li&gt;Step-by-step complex reasoning chains&lt;/li&gt;
&lt;li&gt;Technical problem solving&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;codellama:34b - Meta Code Llama (34 Billion)&lt;/strong&gt;&lt;br&gt;
Size: ~20 GB | RAM needed: 24–32 GB&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama pull codellama:34b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The large version of CodeLlama. For serious developers who need the highest quality local code assistance available. Handles complex codebases, generates tests, writes documentation, and debugs across many languages with significantly higher accuracy than the 13B version.&lt;/p&gt;

&lt;p&gt;Best used for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enterprise-grade code generation&lt;/li&gt;
&lt;li&gt;Complex multi-file code understanding&lt;/li&gt;
&lt;li&gt;Architecture-level coding decisions&lt;/li&gt;
&lt;li&gt;When codellama:13b is not good enough&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;mixtral:8x7b - Mistral AI Mixtral (47 Billion effective)&lt;/strong&gt;&lt;br&gt;
Size: ~26 GB | RAM needed: 32–48 GB&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama pull mixtral:8x7b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A clever architecture called Mixture of Experts - it has 47 billion total parameters but only activates around 13 billion at a time, making it faster than a full 47B model. Excellent at complex reasoning, coding, and multilingual tasks. Delivers near GPT-4-level quality for many tasks.&lt;/p&gt;

&lt;p&gt;Best used for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Complex reasoning and analysis&lt;/li&gt;
&lt;li&gt;High-quality multilingual work&lt;/li&gt;
&lt;li&gt;Advanced coding tasks&lt;/li&gt;
&lt;li&gt;When you want GPT-4-level quality locally and have the RAM&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;llama3.1:70b - Meta Llama 3.1 (70 Billion)&lt;/strong&gt;&lt;br&gt;
Size: ~40 GB | RAM needed: 48 GB or more&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama pull llama3.1:70b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One of the most capable open source models in existence. Competitive with GPT-4 on many benchmarks. Handles complex legal analysis, long-form writing, advanced reasoning, and nuanced understanding at a level the smaller models simply cannot match. Not realistic on a standard laptop - best run on a high-RAM workstation or a machine with a dedicated GPU.&lt;/p&gt;

&lt;p&gt;Best used for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tasks that currently require cloud AI quality&lt;/li&gt;
&lt;li&gt;Complex legal, financial, or medical document analysis&lt;/li&gt;
&lt;li&gt;High-quality long-form content creation&lt;/li&gt;
&lt;li&gt;Advanced multi-step reasoning&lt;/li&gt;
&lt;li&gt;Anything where you need the very best local model available&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Specialised Models
&lt;/h3&gt;




&lt;p&gt;&lt;strong&gt;nomic-embed-text - Nomic (Embedding Model)&lt;/strong&gt;&lt;br&gt;
Size: ~274 MB | RAM needed: Minimal&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama pull nomic-embed-text
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not a chat model - an embedding model. Used by developers to convert text into numerical vectors for semantic search, document similarity, and RAG pipelines. Tiny and fast. Not for conversation but essential for many AI application development workflows.&lt;/p&gt;

&lt;p&gt;Best used for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Building semantic search systems&lt;/li&gt;
&lt;li&gt;Document similarity comparison&lt;/li&gt;
&lt;li&gt;RAG pipeline development&lt;/li&gt;
&lt;li&gt;AI application development&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Full Model Comparison Table
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Parameters&lt;/th&gt;
&lt;th&gt;RAM Needed&lt;/th&gt;
&lt;th&gt;Disk Size&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;gemma2:2b&lt;/td&gt;
&lt;td&gt;2B&lt;/td&gt;
&lt;td&gt;4–6 GB&lt;/td&gt;
&lt;td&gt;1.6 GB&lt;/td&gt;
&lt;td&gt;Fast answers, simple tasks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;qwen2.5:3b&lt;/td&gt;
&lt;td&gt;3B&lt;/td&gt;
&lt;td&gt;6–8 GB&lt;/td&gt;
&lt;td&gt;1.9 GB&lt;/td&gt;
&lt;td&gt;Multilingual, structured data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;llama3.2:3b&lt;/td&gt;
&lt;td&gt;3B&lt;/td&gt;
&lt;td&gt;6–8 GB&lt;/td&gt;
&lt;td&gt;2.0 GB&lt;/td&gt;
&lt;td&gt;General all-rounder&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;phi3:mini&lt;/td&gt;
&lt;td&gt;3.8B&lt;/td&gt;
&lt;td&gt;6–8 GB&lt;/td&gt;
&lt;td&gt;2.2 GB&lt;/td&gt;
&lt;td&gt;Code, JSON, structured output&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;mistral:7b&lt;/td&gt;
&lt;td&gt;7B&lt;/td&gt;
&lt;td&gt;8–12 GB&lt;/td&gt;
&lt;td&gt;4.1 GB&lt;/td&gt;
&lt;td&gt;Strong all-rounder, big quality jump&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;deepseek-coder:6.7b&lt;/td&gt;
&lt;td&gt;6.7B&lt;/td&gt;
&lt;td&gt;8–10 GB&lt;/td&gt;
&lt;td&gt;3.8 GB&lt;/td&gt;
&lt;td&gt;Coding, Python, JS, SQL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;llama3.1:8b&lt;/td&gt;
&lt;td&gt;8B&lt;/td&gt;
&lt;td&gt;10–14 GB&lt;/td&gt;
&lt;td&gt;4.7 GB&lt;/td&gt;
&lt;td&gt;Complex tasks, long documents&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gemma2:9b&lt;/td&gt;
&lt;td&gt;9B&lt;/td&gt;
&lt;td&gt;12–16 GB&lt;/td&gt;
&lt;td&gt;5.4 GB&lt;/td&gt;
&lt;td&gt;Summarisation, factual QA&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;mistral-nemo:12b&lt;/td&gt;
&lt;td&gt;12B&lt;/td&gt;
&lt;td&gt;14–18 GB&lt;/td&gt;
&lt;td&gt;7.1 GB&lt;/td&gt;
&lt;td&gt;Long context, technical writing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;codellama:13b&lt;/td&gt;
&lt;td&gt;13B&lt;/td&gt;
&lt;td&gt;14–18 GB&lt;/td&gt;
&lt;td&gt;7.4 GB&lt;/td&gt;
&lt;td&gt;Code generation and debugging&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;llava:13b&lt;/td&gt;
&lt;td&gt;13B&lt;/td&gt;
&lt;td&gt;14–18 GB&lt;/td&gt;
&lt;td&gt;8.0 GB&lt;/td&gt;
&lt;td&gt;Image understanding&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;phi3:medium&lt;/td&gt;
&lt;td&gt;14B&lt;/td&gt;
&lt;td&gt;14–18 GB&lt;/td&gt;
&lt;td&gt;7.9 GB&lt;/td&gt;
&lt;td&gt;Advanced code and reasoning&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gemma2:27b&lt;/td&gt;
&lt;td&gt;27B&lt;/td&gt;
&lt;td&gt;20–28 GB&lt;/td&gt;
&lt;td&gt;16 GB&lt;/td&gt;
&lt;td&gt;High quality general use&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;deepseek-r1:32b&lt;/td&gt;
&lt;td&gt;32B&lt;/td&gt;
&lt;td&gt;24–32 GB&lt;/td&gt;
&lt;td&gt;19 GB&lt;/td&gt;
&lt;td&gt;Math, logic, analytical reasoning&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;codellama:34b&lt;/td&gt;
&lt;td&gt;34B&lt;/td&gt;
&lt;td&gt;24–32 GB&lt;/td&gt;
&lt;td&gt;20 GB&lt;/td&gt;
&lt;td&gt;Enterprise-grade coding&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;mixtral:8x7b&lt;/td&gt;
&lt;td&gt;47B eff.&lt;/td&gt;
&lt;td&gt;32–48 GB&lt;/td&gt;
&lt;td&gt;26 GB&lt;/td&gt;
&lt;td&gt;Near GPT-4 quality&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;llama3.1:70b&lt;/td&gt;
&lt;td&gt;70B&lt;/td&gt;
&lt;td&gt;48 GB+&lt;/td&gt;
&lt;td&gt;40 GB&lt;/td&gt;
&lt;td&gt;Highest quality, needs workstation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;nomic-embed-text&lt;/td&gt;
&lt;td&gt;Small&lt;/td&gt;
&lt;td&gt;Minimal&lt;/td&gt;
&lt;td&gt;274 MB&lt;/td&gt;
&lt;td&gt;Text embeddings for developers&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Quick Reference - Which Model for Which Task
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Best Model&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Quick everyday questions&lt;/td&gt;
&lt;td&gt;gemma2:2b&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Writing emails and reports&lt;/td&gt;
&lt;td&gt;llama3.2:3b or mistral:7b&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Coding and debugging&lt;/td&gt;
&lt;td&gt;phi3:mini or deepseek-coder:6.7b&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Serious code generation&lt;/td&gt;
&lt;td&gt;codellama:13b or codellama:34b&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Structured data and JSON&lt;/td&gt;
&lt;td&gt;qwen2.5:3b&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multilingual tasks&lt;/td&gt;
&lt;td&gt;qwen2.5:3b or mistral-nemo:12b&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Long document analysis&lt;/td&gt;
&lt;td&gt;llama3.1:8b or mistral-nemo:12b&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mathematical reasoning&lt;/td&gt;
&lt;td&gt;deepseek-r1:32b&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Image understanding&lt;/td&gt;
&lt;td&gt;llava:13b&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fastest response&lt;/td&gt;
&lt;td&gt;gemma2:2b&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best overall on 16 GB RAM&lt;/td&gt;
&lt;td&gt;mistral:7b or llama3.1:8b&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best overall on 32 GB RAM&lt;/td&gt;
&lt;td&gt;deepseek-r1:32b or gemma2:27b&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Closest to GPT-4 quality&lt;/td&gt;
&lt;td&gt;llama3.1:70b or mixtral:8x7b&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  What Should You Run Given Your Machine?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;8 GB RAM:&lt;/strong&gt;&lt;br&gt;
Stick to &lt;code&gt;gemma2:2b&lt;/code&gt;, &lt;code&gt;llama3.2:3b&lt;/code&gt;, or &lt;code&gt;phi3:mini&lt;/code&gt;. These run comfortably and handle most everyday tasks well.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;16 GB RAM (most laptops):&lt;/strong&gt;&lt;br&gt;
Start with &lt;code&gt;llama3.2:3b&lt;/code&gt; or &lt;code&gt;mistral:7b&lt;/code&gt;. Add &lt;code&gt;phi3:mini&lt;/code&gt; for code tasks and &lt;code&gt;deepseek-coder:6.7b&lt;/code&gt; if you write a lot of code. You can also push to &lt;code&gt;llama3.1:8b&lt;/code&gt; if you close other applications while running it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;32 GB RAM:&lt;/strong&gt;&lt;br&gt;
You can run &lt;code&gt;llama3.1:8b&lt;/code&gt;, &lt;code&gt;deepseek-r1:32b&lt;/code&gt;, or &lt;code&gt;gemma2:27b&lt;/code&gt; comfortably. This is where local models start feeling genuinely close to cloud AI quality. The difference from 16 GB models is very noticeable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;32 GB RAM + GPU:&lt;/strong&gt;&lt;br&gt;
Ollama automatically uses your GPU if one is present. Models that took 30 seconds on CPU now respond in 2 to 3 seconds. At this point local AI becomes a serious daily driver that rivals cloud services on speed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;48 GB RAM or more:&lt;/strong&gt;&lt;br&gt;
You can run &lt;code&gt;llama3.1:70b&lt;/code&gt; - one of the best open source models available anywhere. At this level you are genuinely competitive with GPT-4 for many tasks, running completely privately and at zero cost per message.&lt;/p&gt;


&lt;h2&gt;
  
  
  Advantages Over ChatGPT, Claude, and Other Cloud AI
&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;Local (Ollama)&lt;/th&gt;
&lt;th&gt;ChatGPT Plus&lt;/th&gt;
&lt;th&gt;Claude Pro&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Monthly Cost&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;~$20/month&lt;/td&gt;
&lt;td&gt;~$20/month&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Privacy&lt;/td&gt;
&lt;td&gt;100% local&lt;/td&gt;
&lt;td&gt;Sent to OpenAI&lt;/td&gt;
&lt;td&gt;Sent to Anthropic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Works Offline&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rate Limits&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Response Speed&lt;/td&gt;
&lt;td&gt;Moderate (CPU)&lt;/td&gt;
&lt;td&gt;Very fast&lt;/td&gt;
&lt;td&gt;Very fast&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Image Input&lt;/td&gt;
&lt;td&gt;llava only&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Web Search&lt;/td&gt;
&lt;td&gt;No (built-in)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Model Choice&lt;/td&gt;
&lt;td&gt;Full control&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost at Scale&lt;/td&gt;
&lt;td&gt;Always free&lt;/td&gt;
&lt;td&gt;Expensive&lt;/td&gt;
&lt;td&gt;Expensive&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;


&lt;h2&gt;
  
  
  Disadvantages - Being Honest
&lt;/h2&gt;

&lt;p&gt;Local models are impressive but they are not a complete replacement for cloud AI in every situation. Here is what you should know:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;They are slower.&lt;/strong&gt; Cloud AI runs on massive GPU clusters. Your CPU takes longer to generate responses. Expect 5 to 30 seconds per paragraph depending on model size. Fine for most tasks, but slower than ChatGPT's near-instant replies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Smaller models make more mistakes.&lt;/strong&gt; The 2B to 4B models sometimes confidently state incorrect information. The larger the model the more reliable it becomes, but always verify important facts before acting on them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No real-time information.&lt;/strong&gt; These models were trained on data up to a certain date and cannot browse the internet. They cannot tell you today's news, current prices, or recent events.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Large models need large hardware.&lt;/strong&gt; The most capable models require 32 GB or 48 GB RAM. These are not realistic on a standard laptop. If you only have 16 GB you are limited to the small and medium range.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First run is slow.&lt;/strong&gt; The first time you run a model after starting Ollama, it loads from disk into RAM. This can take 10 to 30 seconds. After that, responses are faster.&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 1 - Install Ollama on Windows
&lt;/h2&gt;

&lt;p&gt;Open your browser and go to &lt;code&gt;https://ollama.com/download&lt;/code&gt; and click &lt;strong&gt;Download for Windows&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Run the installer called &lt;code&gt;OllamaSetup.exe&lt;/code&gt;. It installs quietly and starts a local server on your machine at &lt;code&gt;http://localhost:11434&lt;/code&gt;. You will see the Ollama icon appear in your system tray.&lt;/p&gt;

&lt;p&gt;Verify it installed correctly by opening PowerShell and running:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;You should see a version number printed. If you see an error, close and reopen PowerShell and try again.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2 - Download Your First Model
&lt;/h2&gt;

&lt;p&gt;In PowerShell, run this to download the recommended all-rounder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama pull llama3.2:3b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then download others as needed. Each is around 2 to 8 GB:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama pull mistral:7b
ollama pull phi3:mini
ollama pull gemma2:2b
ollama pull qwen2.5:3b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See all downloaded models on your machine with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 3 - Chat via the Terminal
&lt;/h2&gt;

&lt;p&gt;Start a conversation directly in PowerShell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama run llama3.2:3b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will see a &lt;code&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/code&gt; prompt. Just type your question and press Enter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; What is the difference between RAM and storage?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model responds. Ask follow-up questions just like ChatGPT. To exit:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Try the other models the same way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama run mistral:7b
ollama run phi3:mini
ollama run gemma2:2b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Useful terminal commands:&lt;/p&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;ollama list&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show all downloaded models&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ollama run llama3.2:3b&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start chatting with a model&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ollama pull mistral:7b&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Download a new model&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ollama rm gemma2:2b&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Delete a model to free disk space&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ollama serve&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Manually start the API if it stopped&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Step 4 - Use a Proper Chat Interface in the Browser
&lt;/h2&gt;

&lt;p&gt;The terminal works but a full browser-based interface is much more comfortable for everyday use. &lt;strong&gt;Open WebUI&lt;/strong&gt; gives you a complete ChatGPT-style interface - model selector, conversation history, dark mode, everything - running entirely on your machine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Install Open WebUI:&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;open-webui
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Start it:&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;open-webui serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Leave this terminal window open. Then open your browser and go 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;On your first visit you will be asked to create a local account. Use any username, email, and password you like - this account is stored entirely on your machine, nothing goes anywhere.&lt;/p&gt;

&lt;p&gt;Once logged in you will see a full chat interface. Click the model dropdown at the top and select whichever model you want, then start chatting normally.&lt;/p&gt;

&lt;p&gt;To stop Open WebUI, press &lt;code&gt;Ctrl + C&lt;/code&gt; in the terminal window running it. To start it again next time, run &lt;code&gt;open-webui serve&lt;/code&gt; and visit &lt;code&gt;http://localhost:8080&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Tips for Getting Better Responses
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Be specific about what you want.&lt;/strong&gt;&lt;br&gt;
Instead of "help me with this email" try "rewrite this email in a professional tone, keep it under 100 words, and make the call to action clear."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tell it the format you want.&lt;/strong&gt;&lt;br&gt;
"Explain this in bullet points." "Give me a table comparing these options." "Respond in plain language for a non-technical audience."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Give it context.&lt;/strong&gt;&lt;br&gt;
"I am a software engineer working on a Python API. Explain this error message and suggest a fix."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ask it to think step by step.&lt;/strong&gt;&lt;br&gt;
Simply adding "walk me through this step by step" significantly improves reasoning quality on smaller models.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Switch models for different tasks.&lt;/strong&gt;&lt;br&gt;
Use gemma2:2b for quick questions, phi3:mini or deepseek-coder for code, llama3.2:3b or mistral:7b for general writing, qwen2.5:3b for structured output, deepseek-r1:32b for analytical reasoning if your machine can handle it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Upgrade your model when results disappoint.&lt;/strong&gt;&lt;br&gt;
If a response is not good enough, try the next size up. The quality difference between a 3B and 8B model on the same task is often significant.&lt;/p&gt;




&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Does it use the internet after the models are downloaded?&lt;/strong&gt;&lt;br&gt;
No. Everything runs completely offline. Nothing you type leaves your machine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Will it slow down my computer?&lt;/strong&gt;&lt;br&gt;
During inference your CPU usage will spike for a few seconds while generating a response. Other tasks may feel slightly sluggish during that time. Afterwards it returns to normal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I use it for sensitive work documents?&lt;/strong&gt;&lt;br&gt;
Yes. Nothing leaves your machine, making it suitable for confidential documents, client data, and internal company information.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What if I want a more capable model than the ones listed?&lt;/strong&gt;&lt;br&gt;
Browse the full library at &lt;code&gt;https://ollama.com/library&lt;/code&gt;. New models are added regularly as the open source AI community releases them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I update a model when a newer version comes out?&lt;/strong&gt;&lt;br&gt;
Just pull it again: &lt;code&gt;ollama pull llama3.2:3b&lt;/code&gt;. Ollama downloads the updated version automatically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I run multiple models at the same time?&lt;/strong&gt;&lt;br&gt;
Yes, but each model loaded into RAM takes up its full allocation. On 16 GB RAM, running two 3B models simultaneously would use most of your available memory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What if I have a GPU?&lt;/strong&gt;&lt;br&gt;
Ollama detects your GPU automatically and uses it. You do not need to configure anything. Response times drop dramatically - from 15 to 30 seconds down to 1 to 3 seconds on a mid-range GPU.&lt;/p&gt;




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

&lt;p&gt;Running AI models locally on Windows is simpler than most people expect. Install one tool, download a model, and you are ready to go. The whole setup takes under 30 minutes.&lt;/p&gt;

&lt;p&gt;You now have a private, free, offline AI assistant that runs on hardware you already own. It will not replace cloud AI for every task - complex reasoning and real-time information are still better handled by GPT-4 or Claude - but for the majority of everyday questions, writing assistance, coding help, and data tasks, your local model will serve you very well.&lt;/p&gt;

&lt;p&gt;Start with &lt;code&gt;llama3.2:3b&lt;/code&gt; for general use. Move to &lt;code&gt;mistral:7b&lt;/code&gt; when you want better quality. Add &lt;code&gt;phi3:mini&lt;/code&gt; for code. Explore the larger models as your needs grow. And enjoy having your own AI that answers to nobody but you.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Browse the full model library at &lt;code&gt;https://ollama.com/library&lt;/code&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>openai</category>
      <category>claude</category>
      <category>pgaichallenge</category>
    </item>
    <item>
      <title>KCB Funds Transfer API</title>
      <dc:creator>Sospeter Mong'are</dc:creator>
      <pubDate>Mon, 31 Aug 2026 15:00:28 +0000</pubDate>
      <link>https://dev.to/msnmongare/kcb-funds-transfer-api-4ja8</link>
      <guid>https://dev.to/msnmongare/kcb-funds-transfer-api-4ja8</guid>
      <description>&lt;p&gt;The KCB Funds Transfer API enables businesses and applications to securely initiate funds transfers programmatically without requiring manual intervention through the KCB banking platform (Mobile app and Web). This guide walks you through the API, request parameters, authentication, transaction flow, and how to successfully integrate and test a funds transfer.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Is the KCB Funds Transfer API?
&lt;/h2&gt;

&lt;p&gt;Banks have always let you move money through their apps or teller counters. The &lt;a href="https://sandbox.buni.kcbgroup.com/devportal/apis/372552ef-5ebd-4921-9a0d-2f3b1da8cb86/overview" rel="noopener noreferrer"&gt;KCB Funds Transfer (FT) API&lt;/a&gt; lets you do that same thing &lt;strong&gt;from your own application&lt;/strong&gt; - by writing code.&lt;/p&gt;

&lt;p&gt;Instead of a human clicking "Send Money" in the KCB app, your system sends an HTTP request to KCB's servers, and the money moves. That's it at the core.&lt;/p&gt;

&lt;p&gt;This is useful when you're building:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A business payment system (e.g. auto-paying suppliers)&lt;/li&gt;
&lt;li&gt;A SaaS platform that needs to disburse funds to users&lt;/li&gt;
&lt;li&gt;A financial dashboard that triggers bank transfers&lt;/li&gt;
&lt;li&gt;Any system where money movement needs to be automated&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What Can It Actually Do?
&lt;/h2&gt;

&lt;p&gt;The KCB FT API supports four types of money movement:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Transfer Type&lt;/th&gt;
&lt;th&gt;What It Means&lt;/th&gt;
&lt;th&gt;Code&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Internal Transfer&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Between two KCB accounts&lt;/td&gt;
&lt;td&gt;&lt;code&gt;IF&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;RTGS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Large-value transfers to other banks&lt;/td&gt;
&lt;td&gt;&lt;code&gt;RT&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;EFT&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Standard transfers to other banks&lt;/td&gt;
&lt;td&gt;&lt;code&gt;EF&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pesalink&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Bank-to-bank transfers within Kenya's IPS&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PL&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Mobile Money&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;From KCB account to M-PESA wallet&lt;/td&gt;
&lt;td&gt;&lt;code&gt;MO&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;You pick the type that fits your use case and pass the right code in your request.&lt;/p&gt;




&lt;h2&gt;
  
  
  Before You Write Any Code
&lt;/h2&gt;

&lt;p&gt;There are two environments: &lt;strong&gt;Sandbox&lt;/strong&gt; (for testing) and &lt;strong&gt;Production&lt;/strong&gt; (real money). Always start with Sandbox.&lt;/p&gt;

&lt;h3&gt;
  
  
  To Get Started on Sandbox
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Register on the KCB Buni developer portal: &lt;a href="https://sandbox.buni.kcbgroup.com/devportal/apis" rel="noopener noreferrer"&gt;sandbox.buni.kcbgroup.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Create an application on the portal&lt;/li&gt;
&lt;li&gt;Subscribe to the FT API - you'll get a &lt;strong&gt;Consumer Key&lt;/strong&gt; and a &lt;strong&gt;Consumer Secret&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Have a &lt;strong&gt;Callback URL&lt;/strong&gt; ready (more on this below)&lt;/li&gt;
&lt;li&gt;KCB will whitelist your Buni username and provide test account numbers&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  GOING LIVE (For Production (Real Money))
&lt;/h3&gt;

&lt;p&gt;You'll additionally need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A real KCB Bank account number&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://sandbox.buni.kcbgroup.com/devportal/apis/372552ef-5ebd-4921-9a0d-2f3b1da8cb86/documents/7fa2ec48-09f8-425e-974c-d3a29b3c4c5f" rel="noopener noreferrer"&gt;signed Indemnity Form&lt;/a&gt; (KCB provides the template)&lt;/li&gt;
&lt;li&gt;An &lt;a href="https://sandbox.buni.kcbgroup.com/devportal/apis/372552ef-5ebd-4921-9a0d-2f3b1da8cb86/documents/30794f68-11c3-4881-ae72-9ba541b2ab21" rel="noopener noreferrer"&gt;FT Request Letter&lt;/a&gt; for integration approval&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  How the API Works - The Big Picture
&lt;/h2&gt;

&lt;p&gt;The KCB FT API is &lt;strong&gt;asynchronous&lt;/strong&gt;. This is the most important thing to understand as a beginner.&lt;/p&gt;

&lt;p&gt;When you send a transfer request, you don't get back "success" or "failed" immediately. Instead:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You send the transfer request → KCB immediately replies: &lt;em&gt;"Got it, we're processing"&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;KCB processes the transfer in their core banking system&lt;/li&gt;
&lt;li&gt;KCB sends the &lt;strong&gt;result&lt;/strong&gt; to your Callback URL via a POST request&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This means your system needs to be able to &lt;strong&gt;receive incoming requests&lt;/strong&gt; (a webhook endpoint), not just make outgoing ones. If you don't have a callback URL, you won't know whether transfers succeeded or failed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Your App  ──POST──▶  KCB FT API  ──▶  Core Banking
                         │
                         └──POST──▶  Your Callback URL
                                      (result arrives here)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 1: Get an Auth Token
&lt;/h2&gt;

&lt;p&gt;Every API call to KCB requires a &lt;strong&gt;Bearer Token&lt;/strong&gt;. You get this by calling the token endpoint using your Consumer Key and Consumer Secret from the Buni portal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST https://uat.buni.kcbgroup.com/token?grant_type=client_credentials
Authorization: Basic &amp;lt;base64(consumerKey:consumerSecret)&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response gives you an access token. Include it as a header in all subsequent requests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Authorization: Bearer &amp;lt;your_token_here&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tokens expire, so your application should handle token refresh - request a new one when the old one stops working.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: Send a Transfer Request
&lt;/h2&gt;

&lt;p&gt;Once you have a token, you can initiate a funds transfer. Here's what the request body looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"beneficiaryDetails"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"John Doe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"companyCode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"KE0010001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"creditAccountNumber"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1234567890"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"KES"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"debitAccountNumber"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"9876543210"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"debitAmount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"paymentDetails"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Invoice #1042 settlement"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"transactionReference"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"MYAPP20240831001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"transactionType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"IF"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"beneficiaryBankCode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"01"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's break down each field:&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 It Is&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;beneficiaryDetails&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Full name of who receives the money&lt;/td&gt;
&lt;td&gt;&lt;code&gt;"John Doe"&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;companyCode&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;KCB's internal code for your bank branch&lt;/td&gt;
&lt;td&gt;&lt;code&gt;"KE0010001"&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;creditAccountNumber&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Account that receives the money&lt;/td&gt;
&lt;td&gt;&lt;code&gt;"1234567890"&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;currency&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Always &lt;code&gt;KES&lt;/code&gt; for Kenyan Shillings&lt;/td&gt;
&lt;td&gt;&lt;code&gt;"KES"&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;debitAccountNumber&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Your KCB account (the one being charged)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;"9876543210"&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;debitAmount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Amount to transfer (in KES)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;5000&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;paymentDetails&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Short note about the transfer reason&lt;/td&gt;
&lt;td&gt;&lt;code&gt;"Invoice settlement"&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;transactionReference&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Your unique ID&lt;/strong&gt; for this transaction&lt;/td&gt;
&lt;td&gt;&lt;code&gt;"MYAPP20240831001"&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;transactionType&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Type of transfer (see table above)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;"IF"&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;beneficiaryBankCode&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Bank code of the recipient's bank&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;"01"&lt;/code&gt; for KCB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The &lt;code&gt;transactionReference&lt;/code&gt; field is critical.&lt;/strong&gt; It must be unique per transaction - never reuse it. This is how you and KCB track the same transaction. A good pattern: prefix + date + sequential number, e.g. &lt;code&gt;PAY20240831001&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 3: Handle the Response
&lt;/h2&gt;

&lt;p&gt;When your request reaches KCB, they send back an immediate acknowledgement. Here's what success looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"statusCode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"statusMessage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Success"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"statusDescription"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Request received for processing"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"merchantID"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"263eb626-3fe7-4662-813e-f6f2962219e1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"retrievalRefNumber"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PCI663RSS"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;statusCode: "0"&lt;/code&gt; means KCB accepted your request. Save the &lt;code&gt;merchantID&lt;/code&gt; - you can use it to reference this transaction later.&lt;/p&gt;

&lt;p&gt;But &lt;strong&gt;this is not the final result&lt;/strong&gt;. The transfer is still being processed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Common Error Responses
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Error&lt;/th&gt;
&lt;th&gt;What It Means&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Missing Credentials&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Token is missing or wrong&lt;/td&gt;
&lt;td&gt;Re-check your Bearer token&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Validation failed: Invalid crucial param&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A field is missing or wrong value&lt;/td&gt;
&lt;td&gt;Review your request body&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Retrieval RefNumber already exist&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;You reused a &lt;code&gt;transactionReference&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Generate a new unique reference&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Transaction failed due to limit rule&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Amount exceeds your daily/transaction limit&lt;/td&gt;
&lt;td&gt;Adjust amount or check limits on FT portal&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Step 4: Handle the Callback (The Real Result)
&lt;/h2&gt;

&lt;p&gt;When KCB finishes processing, they POST to your Callback URL. This is what a successful notification looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ftReference"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"FT22060GXZGY"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"transactionDate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2022-07-06T11:08:40.019Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"5000"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"transactionStatus"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SUCCESS"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"transactionMessage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Processed Successfully"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"beneficiaryAccountNumber"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1234567890"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"beneficiaryName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"JOHN DOE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"transactionReference"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"MYAPP20240831001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"merchantId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1d2b13f8-ea62-465c-9fda-e18353579880"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"debitAccountNumber"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"9876543210"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your callback endpoint should:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check &lt;code&gt;transactionStatus&lt;/code&gt; - it'll be &lt;code&gt;SUCCESS&lt;/code&gt; or &lt;code&gt;FAILED&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Match &lt;code&gt;transactionReference&lt;/code&gt; to the transfer you initiated&lt;/li&gt;
&lt;li&gt;Update your database accordingly&lt;/li&gt;
&lt;li&gt;Return an HTTP &lt;code&gt;200 OK&lt;/code&gt; so KCB knows you received the notification&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Always match incoming callbacks to your own records using &lt;code&gt;transactionReference&lt;/code&gt;. Never trust a callback blindly - verify the amount and accounts match what you sent.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Sending Money to M-PESA
&lt;/h2&gt;

&lt;p&gt;To transfer from a KCB account to an M-PESA wallet, use transaction type &lt;code&gt;MO&lt;/code&gt; and set &lt;code&gt;beneficiaryBankCode&lt;/code&gt; to &lt;code&gt;MPESA&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"transactionType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"MO"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"beneficiaryBankCode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"MPESA"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"creditAccountNumber"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2547XXXXXXXX"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"beneficiaryDetails"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Jane Wanjiku"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;creditAccountNumber&lt;/code&gt; in this case should be the recipient's phone number in the format &lt;code&gt;2547XXXXXXXX&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Sending to Other Banks
&lt;/h2&gt;

&lt;p&gt;For inter-bank transfers (EFT, RTGS, Pesalink), you need the correct &lt;strong&gt;bank code&lt;/strong&gt; for the recipient's bank. Here are 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;Bank&lt;/th&gt;
&lt;th&gt;Code&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;KCB&lt;/td&gt;
&lt;td&gt;01&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Equity Bank&lt;/td&gt;
&lt;td&gt;68&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Co-op Bank&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NCBA&lt;/td&gt;
&lt;td&gt;07&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stanbic Bank&lt;/td&gt;
&lt;td&gt;31&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;I&amp;amp;M Bank&lt;/td&gt;
&lt;td&gt;57&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DTB&lt;/td&gt;
&lt;td&gt;63&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Family Bank&lt;/td&gt;
&lt;td&gt;70&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Absa&lt;/td&gt;
&lt;td&gt;03&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;M-PESA&lt;/td&gt;
&lt;td&gt;MPESA&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The full list is available in the API documentation appendix.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Minimal Node.js Example
&lt;/h2&gt;

&lt;p&gt;Here's a simple end-to-end example to get you oriented:&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;axios&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;axios&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Step 1: Get a token&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;consumerKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;consumerSecret&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;credentials&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;consumerKey&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;consumerSecret&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;base64&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;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://uat.buni.kcbgroup.com/token?grant_type=client_credentials&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="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Basic &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Step 2: Initiate a transfer&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;sendTransfer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;beneficiaryDetails&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;John Doe&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;companyCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;KE0010001&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;creditAccountNumber&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1234567890&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;KES&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;debitAccountNumber&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;9876543210&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;debitAmount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;paymentDetails&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Test payment&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;transactionReference&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`PAY&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nb"&gt;Date&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="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// unique reference&lt;/span&gt;
    &lt;span class="na"&gt;transactionType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;IF&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;beneficiaryBankCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;01&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;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://uat.buni.kcbgroup.com/ft/transfer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// confirm exact endpoint from KCB&lt;/span&gt;
    &lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="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;application/json&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="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Step 3: Callback handler (Express example)&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/kcb/callback&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;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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;transactionReference&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;transactionStatus&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&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;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transactionStatus&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SUCCESS&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;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="s2"&gt;`Transfer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;transactionReference&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; succeeded - KES &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// update your DB here&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&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="s2"&gt;`Transfer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;transactionReference&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; FAILED`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// handle failure - notify user, reverse any internal state&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;status&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="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;OK&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// always acknowledge&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Common Beginner Mistakes to Avoid
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Not saving the &lt;code&gt;transactionReference&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
If you don't store it, you can't match the callback to your original request.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Confusing "Success" response with a completed transfer&lt;/strong&gt;&lt;br&gt;
A &lt;code&gt;statusCode: "0"&lt;/code&gt; means &lt;em&gt;accepted&lt;/em&gt;, not &lt;em&gt;completed&lt;/em&gt;. Wait for the callback.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Not handling duplicate callbacks&lt;/strong&gt;&lt;br&gt;
KCB may retry sending the callback if your server returns a non-200 response. Make your callback handler &lt;strong&gt;idempotent&lt;/strong&gt; - processing the same &lt;code&gt;transactionReference&lt;/code&gt; twice should not double-credit or double-update.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Using production credentials in development&lt;/strong&gt;&lt;br&gt;
Always use Sandbox for testing. Real money moves in Production.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Hardcoding the &lt;code&gt;transactionReference&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
It must be unique per request. Generate it dynamically.&lt;/p&gt;




&lt;h2&gt;
  
  
  Quick Reference
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;Detail&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sandbox Portal&lt;/td&gt;
&lt;td&gt;sandbox.buni.kcbgroup.com&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Token Endpoint&lt;/td&gt;
&lt;td&gt;&lt;code&gt;POST /token?grant_type=client_credentials&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Auth Method&lt;/td&gt;
&lt;td&gt;OAuth 2.0 (Basic Auth with Consumer Key + Secret)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Request Type&lt;/td&gt;
&lt;td&gt;Async (result comes via callback)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Result Delivery&lt;/td&gt;
&lt;td&gt;POST to your Callback URL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Support Email&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:buni@kcbgroup.com"&gt;buni@kcbgroup.com&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Register on the &lt;a href="https://sandbox.buni.kcbgroup.com/devportal/apis" rel="noopener noreferrer"&gt;KCB Buni portal&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Create an application and subscribe to the FT API&lt;/li&gt;
&lt;li&gt;Set up a test callback URL (tools like &lt;a href="https://webhook.site" rel="noopener noreferrer"&gt;Webhook.site&lt;/a&gt; or &lt;a href="https://ngrok.com" rel="noopener noreferrer"&gt;ngrok&lt;/a&gt; are useful here)&lt;/li&gt;
&lt;li&gt;Run a test internal transfer between the sandbox accounts KCB provides&lt;/li&gt;
&lt;li&gt;Inspect the callback payload your server receives&lt;/li&gt;
&lt;li&gt;Once confident, apply for Production access&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;For questions or integration support, reach out to the KCB Buni team at &lt;strong&gt;&lt;a href="mailto:buni@kcbgroup.com"&gt;buni@kcbgroup.com&lt;/a&gt;&lt;/strong&gt; or &lt;a href="https://wa.me/254708920430?text=Hi+Sos%2C+I+need+help+with+KCB+integrations" rel="noopener noreferrer"&gt;Whatsapp Me here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>kcb</category>
      <category>buni</category>
      <category>stripe</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Cron Expressions in Airflow: Understanding `00 3 * * 1-7`</title>
      <dc:creator>Sospeter Mong'are</dc:creator>
      <pubDate>Wed, 26 Aug 2026 07:49:29 +0000</pubDate>
      <link>https://dev.to/msnmongare/cron-expressions-in-airflow-understanding-00-3-1-7-3cff</link>
      <guid>https://dev.to/msnmongare/cron-expressions-in-airflow-understanding-00-3-1-7-3cff</guid>
      <description>&lt;p&gt;If you are working with &lt;strong&gt;Apache Airflow&lt;/strong&gt;, Linux, automation, or data pipelines, you will eventually encounter &lt;strong&gt;cron expressions&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;At first, something like this can look confusing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;00 3 * * 1-7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But once you understand what each part represents, cron schedules become very easy to read and create.&lt;/p&gt;

&lt;p&gt;In this article, we will break down the cron expression &lt;code&gt;00 3 * * 1-7&lt;/code&gt;, explain each component, and show how it is used in Airflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is a Cron Expression?
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;cron expression&lt;/strong&gt; is a format used to define when a scheduled task should run.&lt;/p&gt;

&lt;p&gt;Cron is commonly used in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Linux and Unix systems&lt;/li&gt;
&lt;li&gt;Apache Airflow&lt;/li&gt;
&lt;li&gt;Data engineering pipelines&lt;/li&gt;
&lt;li&gt;Database jobs&lt;/li&gt;
&lt;li&gt;ETL and ELT processes&lt;/li&gt;
&lt;li&gt;Backup automation&lt;/li&gt;
&lt;li&gt;Application maintenance&lt;/li&gt;
&lt;li&gt;Server monitoring&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A traditional cron expression contains &lt;strong&gt;five fields&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;┌───── Minute
│ ┌─── Hour
│ │ ┌─ Day of month
│ │ │ ┌─ Month
│ │ │ │ ┌─ Day of week
│ │ │ │ │
00  3  *  *  1-7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each field tells the scheduler a different part of the schedule.&lt;/p&gt;




&lt;h1&gt;
  
  
  Understanding &lt;code&gt;00 3 * * 1-7&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;Let's break it down:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;00 3 * * 1-7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;Value&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;Minute&lt;/td&gt;
&lt;td&gt;&lt;code&gt;00&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;At minute 0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hour&lt;/td&gt;
&lt;td&gt;&lt;code&gt;3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;At 3 AM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Day of month&lt;/td&gt;
&lt;td&gt;&lt;code&gt;*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Every day&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Month&lt;/td&gt;
&lt;td&gt;&lt;code&gt;*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Every month&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Day of week&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1-7&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Monday through Sunday&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Therefore:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;00 3 * * 1-7&lt;/code&gt; means run the task every day at 3:00 AM.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  1. The Minute Field - &lt;code&gt;00&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The first field represents the &lt;strong&gt;minute&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;00
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means the task runs at minute zero.&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;3:00 AM
4:00 AM
5:00 AM
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you used:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;instead, the task would run at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;3:30 AM
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

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

&lt;/div&gt;



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

&lt;blockquote&gt;
&lt;p&gt;At exactly 3:00.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  2. The Hour Field - &lt;code&gt;3&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The second field represents the &lt;strong&gt;hour&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;3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means 3 AM.&lt;/p&gt;

&lt;p&gt;Combined with the first field:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



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

&lt;blockquote&gt;
&lt;p&gt;3:00 AM.&lt;/p&gt;
&lt;/blockquote&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;00 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;means 1:00 AM.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;means 6:00 AM.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;means 6:30 PM.&lt;/p&gt;

&lt;p&gt;Cron generally uses the &lt;strong&gt;24-hour clock&lt;/strong&gt;, so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;3  = 3 AM
12 = 12 PM
15 = 3 PM
18 = 6 PM
23 = 11 PM
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  3. Day of Month - &lt;code&gt;*&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;The third field represents the &lt;strong&gt;day of the month&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;*
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The asterisk means:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Every possible value.&lt;/p&gt;
&lt;/blockquote&gt;

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

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

&lt;/div&gt;



&lt;p&gt;in this position means every day of the month:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1
2
3
4
...
31
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means there is no restriction based on the date.&lt;/p&gt;




&lt;h1&gt;
  
  
  4. Month - &lt;code&gt;*&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;The fourth field represents the &lt;strong&gt;month&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Again, we have:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



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

&lt;blockquote&gt;
&lt;p&gt;Every month.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Therefore, the schedule applies to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;January
February
March
April
...
December
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is no restriction on the month.&lt;/p&gt;




&lt;h1&gt;
  
  
  5. Day of Week - &lt;code&gt;1-7&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;The final field represents the &lt;strong&gt;day of the week&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;1-7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The range means Monday through Sunday.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1 = Monday
2 = Tuesday
3 = Wednesday
4 = Thursday
5 = Friday
6 = Saturday
7 = Sunday
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

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

&lt;/div&gt;



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

&lt;blockquote&gt;
&lt;p&gt;Every day of the week.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So the entire expression:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;00 3 * * 1-7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Run every day at 3:00 AM.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  Cron Schedule Examples
&lt;/h1&gt;

&lt;p&gt;Understanding a few examples makes cron much easier.&lt;/p&gt;

&lt;h3&gt;
  
  
  Every day at 3:00 AM
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;00 3 * * *
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Meaning:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Run every day at 3:00 AM.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You could also write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;00 3 * * 1-7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both express a daily schedule.&lt;/p&gt;

&lt;h3&gt;
  
  
  Every day at 6:30 AM
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;30 6 * * *
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Meaning:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Run every day at 6:30 AM.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Every Monday at 3:00 AM
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;00 3 * * 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Meaning:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Run every Monday at 3:00 AM.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Every Friday at 5:00 PM
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;00 17 * * 5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Meaning:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Run every Friday at 5:00 PM.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Every hour
&lt;/h3&gt;



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

&lt;/div&gt;



&lt;p&gt;Meaning:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Run at minute 0 of every hour.&lt;/p&gt;
&lt;/blockquote&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;1:00
2:00
3:00
4:00
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Every 15 minutes
&lt;/h3&gt;



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

&lt;/div&gt;



&lt;p&gt;Meaning:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Run every 15 minutes.&lt;/p&gt;
&lt;/blockquote&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;12:00
12:15
12:30
12:45
1:00
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Using Cron Expressions in Apache Airflow
&lt;/h1&gt;

&lt;p&gt;Cron expressions are particularly useful in &lt;strong&gt;Apache Airflow&lt;/strong&gt; because they allow you to control when DAGs should run.&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 python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;airflow&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;DAG&lt;/span&gt;

&lt;span class="n"&gt;dag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DAG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;dag_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;daily_pipeline&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;schedule&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;00 3 * * *&lt;/span&gt;&lt;span class="sh"&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;This tells Airflow to schedule the DAG:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Every day at 3:00 AM.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You may also encounter:&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="n"&gt;schedule&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;00 3 * * 1-7&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This explicitly specifies Monday through Sunday.&lt;/p&gt;

&lt;p&gt;In this particular case, however, the simpler:&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="n"&gt;schedule&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;00 3 * * *&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;is easier to read because the &lt;code&gt;*&lt;/code&gt; already means every day of the week.&lt;/p&gt;




&lt;h1&gt;
  
  
  Be Careful With Timezones in Airflow
&lt;/h1&gt;

&lt;p&gt;One of the most important things to understand when working with &lt;strong&gt;Airflow schedules&lt;/strong&gt; is the timezone.&lt;/p&gt;

&lt;p&gt;Suppose your Airflow environment uses &lt;strong&gt;UTC&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If your DAG is scheduled for:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;03:00 UTC
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and you are working in Kenya, which uses &lt;strong&gt;East Africa Time (EAT)&lt;/strong&gt;, that corresponds to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;06:00 AM EAT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So if your intention is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Run the pipeline at 3:00 AM Kenya time&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;you need to make sure your Airflow DAG and environment are configured with the appropriate timezone.&lt;/p&gt;

&lt;p&gt;This is especially important for data engineering teams working across countries and regions.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why Cron Expressions Matter in Data Engineering
&lt;/h1&gt;

&lt;p&gt;Cron schedules are commonly used to control the execution of data pipelines.&lt;/p&gt;

&lt;p&gt;For example, imagine you have a pipeline that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Extracts data from a source database&lt;/li&gt;
&lt;li&gt;Loads the data into a data lake&lt;/li&gt;
&lt;li&gt;Transforms the data using dbt&lt;/li&gt;
&lt;li&gt;Updates a data warehouse&lt;/li&gt;
&lt;li&gt;Makes the data available for reporting&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You might schedule the pipeline to run every morning:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;00 3 * * *
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The pipeline would then be triggered every day at 3:00 AM.&lt;/p&gt;

&lt;p&gt;This is particularly useful when data needs to be processed before business users start working in the morning.&lt;/p&gt;




&lt;h1&gt;
  
  
  A Simple Way to Read Cron
&lt;/h1&gt;

&lt;p&gt;Whenever you see a cron expression, read it from left to right:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MINUTE
HOUR
DAY OF MONTH
MONTH
DAY OF WEEK
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;00 3 * * 1-7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;you can translate it mentally as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;00    -&amp;gt; minute 0
3     -&amp;gt; hour 3
*     -&amp;gt; every day
*     -&amp;gt; every month
1-7   -&amp;gt; Monday to Sunday
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then combine everything:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Every day at 3:00 AM.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  Quick Cron Cheat Sheet
&lt;/h1&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Cron&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;00 3 * * *&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Every day at 3:00 AM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;00 3 * * 1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Every Monday at 3:00 AM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;00 3 * * 1-5&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Monday to Friday at 3:00 AM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;00 3 * * 1-7&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Every day at 3:00 AM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;30 6 * * *&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Every day at 6:30 AM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;00 12 * * *&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Every day at noon&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;00 18 * * 5&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Every Friday at 6:00 PM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;*/15 * * * *&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Every 15 minutes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;00 * * * *&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Every hour&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;00 0 1 * *&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;First day of every month at midnight&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Final Takeaway
&lt;/h2&gt;

&lt;p&gt;The cron expression:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;00 3 * * 1-7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Run every day of the week at exactly 3:00 AM.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The five cron fields are:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;00    3    *    *    1-7
│     │    │    │      │
│     │    │    │      └── Day of week
│     │    │    └───────── Month
│     │    └────────────── Day of month
│     └─────────────────── Hour
└───────────────────────── Minute
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you understand these five fields, you can read most basic &lt;strong&gt;Linux cron and Apache Airflow schedules&lt;/strong&gt; without having to memorize them.&lt;/p&gt;

</description>
      <category>linux</category>
      <category>airflow</category>
      <category>openai</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Understanding AI Model Pricing</title>
      <dc:creator>Sospeter Mong'are</dc:creator>
      <pubDate>Mon, 24 Aug 2026 12:39:42 +0000</pubDate>
      <link>https://dev.to/msnmongare/understanding-ai-model-pricing-2d4f</link>
      <guid>https://dev.to/msnmongare/understanding-ai-model-pricing-2d4f</guid>
      <description>&lt;p&gt;When you look at AI pricing for example &lt;a href="https://cursor.com/docs/models-and-pricing" rel="noopener noreferrer"&gt;cursor&lt;/a&gt;, you will often see four numbers:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pricing component&lt;/th&gt;
&lt;th&gt;What it means&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Input&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;What you send to the AI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cache Write&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Information saved temporarily so it can be reused&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cache Read&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Previously cached information that the AI reads again&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Output&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;What the AI generates for you&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These are usually priced &lt;strong&gt;per million tokens&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Let's break them down.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Input
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Input is everything you give the AI.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This can include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your question&lt;/li&gt;
&lt;li&gt;Your instructions&lt;/li&gt;
&lt;li&gt;Code you provide&lt;/li&gt;
&lt;li&gt;Files you ask it to analyze&lt;/li&gt;
&lt;li&gt;Previous conversation/context&lt;/li&gt;
&lt;li&gt;Relevant parts of your project&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, you tell Cursor:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Fix this Laravel authentication bug."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And Cursor sends your instructions plus relevant code to the AI.&lt;/p&gt;

&lt;p&gt;That information is &lt;strong&gt;input&lt;/strong&gt;.&lt;/p&gt;

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

&lt;p&gt;Suppose the model's input price is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;$2 per million tokens&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If your request uses 10,000 input tokens:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;10,000 / 1,000,000 × $2
= $0.02
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the input cost would be approximately &lt;strong&gt;$0.02&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  2. Output
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Output is what the AI generates in response.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For a coding assistant, output could include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code&lt;/li&gt;
&lt;li&gt;Explanations&lt;/li&gt;
&lt;li&gt;SQL queries&lt;/li&gt;
&lt;li&gt;Suggestions&lt;/li&gt;
&lt;li&gt;Refactored code&lt;/li&gt;
&lt;li&gt;JSON&lt;/li&gt;
&lt;li&gt;Documentation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Suppose a model costs:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;$6 per million output tokens&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If it generates 10,000 tokens:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;10,000 / 1,000,000 × $6
= $0.06
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the output cost would be approximately &lt;strong&gt;$0.06&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why is Output often more expensive?
&lt;/h3&gt;

&lt;p&gt;Generating information is computationally expensive.&lt;/p&gt;

&lt;p&gt;That's why you will often see something like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Input: $2/M&lt;br&gt;
Output: $6/M&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The AI charges differently for what it &lt;strong&gt;reads&lt;/strong&gt; and what it &lt;strong&gt;generates&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  3. Cache Write
&lt;/h1&gt;

&lt;p&gt;This one is slightly more complicated.&lt;/p&gt;

&lt;p&gt;Imagine you are working on a large Laravel project.&lt;/p&gt;

&lt;p&gt;Every time you ask Cursor something, Cursor may need to provide the AI with a lot of context about your project.&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;Laravel project
+ routes
+ controllers
+ models
+ migrations
+ services
+ configuration
+ existing code
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sending the same information repeatedly can be inefficient.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Caching allows some of that information to be stored temporarily so it can be reused.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When information is stored in the cache, that is called a:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cache Write&lt;/strong&gt;&lt;/p&gt;

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

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Cache Write = putting information into the AI's reusable memory/cache.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  4. Cache Read
&lt;/h1&gt;

&lt;p&gt;Once information has been cached, the AI can reuse it.&lt;/p&gt;

&lt;p&gt;Instead of processing the information as completely new input again, it can read the cached information.&lt;/p&gt;

&lt;p&gt;That's:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cache Read&lt;/strong&gt;&lt;/p&gt;

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

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Cache Read = using information that was already stored in the cache.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is usually cheaper than sending the same information as completely new input.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Input = $2 / million tokens
Cache Read = $0.50 / million tokens
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you repeatedly work with the same project context, using the cache can significantly reduce the cost of processing that repeated context.&lt;/p&gt;




&lt;h1&gt;
  
  
  5. A Simple Real-World Example
&lt;/h1&gt;

&lt;p&gt;Imagine you are building a Laravel application in Cursor.&lt;/p&gt;

&lt;p&gt;You ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Create an API endpoint for registering users."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Cursor might send:&lt;/p&gt;

&lt;h3&gt;
  
  
  Input
&lt;/h3&gt;

&lt;p&gt;Your instructions + relevant Laravel code.&lt;/p&gt;

&lt;p&gt;Then the AI generates:&lt;/p&gt;

&lt;h3&gt;
  
  
  Output
&lt;/h3&gt;

&lt;p&gt;The controller, request validation, route and other code.&lt;/p&gt;

&lt;p&gt;Now you ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Add email verification."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Some of the project context may already be cached.&lt;/p&gt;

&lt;p&gt;So instead of treating everything as completely new information, Cursor may use:&lt;/p&gt;

&lt;h3&gt;
  
  
  Cache Read
&lt;/h3&gt;

&lt;p&gt;to reuse previously processed context.&lt;/p&gt;

&lt;p&gt;If Cursor needs to add new information to the cache, that's:&lt;/p&gt;

&lt;h3&gt;
  
  
  Cache Write
&lt;/h3&gt;




&lt;h1&gt;
  
  
  6. Why Cache Pricing Can Be Confusing
&lt;/h1&gt;

&lt;p&gt;You might see pricing like:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Price per million tokens&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Input&lt;/td&gt;
&lt;td&gt;$2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cache Write&lt;/td&gt;
&lt;td&gt;$2.50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cache Read&lt;/td&gt;
&lt;td&gt;$0.20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Output&lt;/td&gt;
&lt;td&gt;$10&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;At first, this can look confusing.&lt;/p&gt;

&lt;p&gt;You might ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Why am I being charged four different prices?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Because the AI provider is measuring &lt;strong&gt;different types of token processing&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Think about it like a database:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Input&lt;/strong&gt; = sending data to the database&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache Write&lt;/strong&gt; = storing frequently used data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache Read&lt;/strong&gt; = retrieving stored data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Output&lt;/strong&gt; = generating the result&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The exact implementation of caching differs between AI providers, but this mental model is useful for understanding the pricing table.&lt;/p&gt;




&lt;h1&gt;
  
  
  7. Putting Everything Together
&lt;/h1&gt;

&lt;p&gt;Let's say you use a model with:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Price&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Input&lt;/td&gt;
&lt;td&gt;$2/M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cache Write&lt;/td&gt;
&lt;td&gt;$2.50/M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cache Read&lt;/td&gt;
&lt;td&gt;$0.20/M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Output&lt;/td&gt;
&lt;td&gt;$10/M&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;During one interaction, suppose you use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;20,000 input tokens&lt;/li&gt;
&lt;li&gt;5,000 cache-write tokens&lt;/li&gt;
&lt;li&gt;50,000 cache-read tokens&lt;/li&gt;
&lt;li&gt;10,000 output tokens&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The approximate cost would be:&lt;/p&gt;

&lt;h3&gt;
  
  
  Input
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;20,000 / 1,000,000 × $2
= $0.04
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Cache Write
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;5,000 / 1,000,000 × $2.50
= $0.0125
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Cache Read
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;50,000 / 1,000,000 × $0.20
= $0.01
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Output
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;10,000 / 1,000,000 × $10
= $0.10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Total
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$0.04 + $0.0125 + $0.01 + $0.10
= $0.1625
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So that interaction would cost approximately:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;$0.16&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The important lesson is that &lt;strong&gt;you don't simply multiply the model's headline price by the number of times you use it&lt;/strong&gt;. The actual cost depends on how many tokens are processed in each category.&lt;/p&gt;




&lt;h1&gt;
  
  
  8. Which One Should You Pay Most Attention To?
&lt;/h1&gt;

&lt;p&gt;As a beginner, focus on these two first:&lt;/p&gt;

&lt;h3&gt;
  
  
  Input
&lt;/h3&gt;

&lt;p&gt;How much information are you asking the AI to process?&lt;/p&gt;

&lt;h3&gt;
  
  
  Output
&lt;/h3&gt;

&lt;p&gt;How much information is the AI generating?&lt;/p&gt;

&lt;p&gt;These are the easiest to understand.&lt;/p&gt;

&lt;p&gt;Then learn:&lt;/p&gt;

&lt;h3&gt;
  
  
  Cache Read
&lt;/h3&gt;

&lt;p&gt;This can help reduce the cost of repeatedly processing the same context.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cache Write
&lt;/h3&gt;

&lt;p&gt;This is related to putting information into the cache so it can potentially be reused.&lt;/p&gt;




&lt;h1&gt;
  
  
  9. Why This Matters When Using Cursor
&lt;/h1&gt;

&lt;p&gt;This becomes especially important when you use &lt;strong&gt;Agent mode&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Suppose you ask Cursor to:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Build a complete payment integration."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Cursor may need to understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your existing application&lt;/li&gt;
&lt;li&gt;Your database&lt;/li&gt;
&lt;li&gt;Existing controllers&lt;/li&gt;
&lt;li&gt;Routes&lt;/li&gt;
&lt;li&gt;Models&lt;/li&gt;
&lt;li&gt;Configuration&lt;/li&gt;
&lt;li&gt;Payment code&lt;/li&gt;
&lt;li&gt;Documentation&lt;/li&gt;
&lt;li&gt;Your instructions&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;If you then continue asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Now add callbacks."&lt;/p&gt;

&lt;p&gt;"Now add transaction validation."&lt;/p&gt;

&lt;p&gt;"Now add retries."&lt;/p&gt;

&lt;p&gt;"Now write tests."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Cursor may repeatedly work with a large amount of project context.&lt;/p&gt;

&lt;p&gt;This is one reason why &lt;strong&gt;Agent-heavy users can consume significantly more AI usage than developers who only use autocomplete or ask small questions.&lt;/strong&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  10. The Easiest Way to Remember It
&lt;/h1&gt;

&lt;p&gt;Think of an AI conversation like this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Input&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;"Here is what I want and here is the information you need."&lt;/p&gt;

&lt;p&gt;↓&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cache Write&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;"Store some of this information so we can reuse it."&lt;/p&gt;

&lt;p&gt;↓&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cache Read&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;"Use information we already stored."&lt;/p&gt;

&lt;p&gt;↓&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Output&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;"Here is the answer/code I generated."&lt;/p&gt;

&lt;p&gt;And because each operation can have a different computational cost, &lt;strong&gt;each can have a different price per million tokens&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That is what the four columns in Cursor's model pricing table are telling you.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>beginners</category>
      <category>productivity</category>
      <category>python</category>
    </item>
  </channel>
</rss>
